[原址]http://www.gekko-software.nl/DotNet/Art01.htm
An (incomplete) comparison of Delphi and C# features
Delphi and C# are both rich and powerful object oriented programming languages. As a reminder a short recapitulation of what that stands for : Objects are a grouping of data and methods (functions) working on that data. Objects are based on classes. A class is a declaration of the data and the code of the methods. A class will create objects using constructor methods.
An real object oriented language supports encapsulation, inheritance and polymorphism. Encapsulation hides everything inside an object except what you explicitly want to show to the outer world. Inheritance lets you re-use a class to create a derived class with added functionality. Polymorphism lets you re-use a class and alter its exposed behavior.
Delphi has a great support for object orientation but you can write a plain Pascal program without using any object at all. In C# everything is an object, including the application itself. Running a C# program means creating an object and executing the object's main method. C# does not know how to handle any "loose" procedures or constants either, everything has to belong to a class.
The Delphi-VCL couple shares a lot with the C#-.NET couple which makes a comparison on items possible. This list is by far incomplete, it is just a road-sketch from a Delphi perspective:
Both have
-
Encapsulation
To hide the internals of a class you declare your variables in Delphi as private or protected. A private is only visible inside the class, a protected is also visible inside the descendent of the class. C# uses these same keywords for the same purpose.
-
Inheritance
Both Delphi and C# support single inheritance, a class can have only one base class.
-
Polymorphism
You can override a method in Delphi or C# and so change the behavior of a descendent class, provided the method is marked as virtual in the base class. In the descendent class the method should be marked using the override directive. The inherited code will be available in the overriden method. To hide the inherited method Delphi uses the reintroduce directive, which is named new in C#. Delphi dynamic methods follow the same rules as virtual, the only differences are in the implementation of the actual object's method table.
In C# you can end the possibility to override any methods in further descendents of the class by marking the class as sealed. This is a nice way to publish a class without the uncertainty that it's behavior will be broken.
-
Class (static) methods
Class methods operate on the class itself and don't need an object of that class to be executed. Typical class methods are constructors, named constructor in Delphi. In C# the constructor is a method with the same name as the class. The names of other class methods are prefixed with class in Delphi and with static in C#. The C# keyword static does also have the same meaning as static in Delphi, you cannot override a C# static method. But you can override a class method in Delphi.
-
Object initialization
In a hierarchy of objects every derived class class can introduce a new constructor. Which constructors actually get executed differs between the two languages, in Delphi each constructor has to be called explicitly. Besides the constructors Delphi's tObject base class has a method AfterConstruction, which will be executed after the last constructor has finished. So there are many places where a Delphi object can be initialized.
In C# the constructor of the ultimate base class System.object always will be executed first, the execution of a constructor of any other base class has to be explicitly asked for in the declaration of the constructor as ": base." There is no AfterConstruction in C#, everything has to be (and can be) done in the constructors themselves.
Type
tMyClass = class(tObject)
constructor createEx;
end;
tMyClass2 = class(tMyClass)
constructor createEx; override;
procedure AfterConstruction override;
end;
constructor tMyClass.CreateEx;
begin
...
end;
constructor tMyClass2.CreateEx;
begin
inherited CreateEx;
...
end;
procedure tMyClass2.AfterConstruction
begin
inherited;
// Initialization
end;public class MyClass
{
public MyClass(int anInt)
{...}
}
public class MyClass2 : MyClass
{
public MyClass2(int anInt) : base(anInt)
{
...
// Initialization
}
}
-
Object finalization
The lifespan of an object is very different in Delphi and .NET. In Delphi you have to explicitly free an object by making a call to (tObject.) Free which destroys the object by calling its destructor. Failing to do so leads to resource leaks. Resources like memory, forms, database handles and everything else your object has been using. In the VCL many classes are based on tComponent, objects of this class can own other objects. When a tComponent object is freed it will free all objects it owns, the class takes a lot of pain out of object management in Delphi. Besides overriding the destructor itself, tObject has a nice BeforeDestruction method which will fire before the first destructor will execute. This method is a good place to free resources.
In .NET objects can only be destroyed by the garbage collector. Inside the garbage collector there is a separate finalizer thread running which will call the finalizer on all objects who are no longer referenced by any code. Finalizers can be compared to the destructors in Delphi but it is strongly advised not to use or rely on them. The main reason for this is that it is completely unknown when and in what order the objects will be destroyed. The garbage collector can be manipulated by the application, the actual order in which the objects will be destroyed cannot. Any resources used by the objects, like database connections or windows handles will not be freed until the garbage collector has decided it time to destroy the object. Which could be hours after the application itself terminated. As a solution .NET has the Idisposable interface. Any class which implements dispose, the only method of this interface, can free up its resources in the dispose implementation. Many classes in the framework implement the interface and calls to Dispose pop up everywhere in code generated by VS.NET wizards and in the .NET documentation.
For an elaborate discussion on the differences between object finalization in C# and Delphi and how Delphi for .NET bridges the gap there is a very good article by Brian Long on the Borland developer network site.
-
Overloading
In both languages a method can be declared several times as long as every declaration has a different signature. In Delphi an overloaded method has to be marked with the keyword overload;
procedure MyMethod; overload;
procedure MyMethod(MyParam : integer); overload;public MyMethod()
public MyMethod(int MyMethod)Constructors can be overloaded as well.
constructor Create; overload;
constructor Create(MyInitParam : integer); overload;public MyClass()
public MyClass(int MyInitParam)
-
Abstract methods
An abstract method is a declaration of a method without an implementation. The implementation has to be provided by the descendants. A C# class which has any abstract methods has to be marked as being an abstract class.
-
Interfaces
An interface is like a class with only abstract members and is in both languages an integral part of the language. In both languages a class can implement any number of interfaces, the interface list is part of the class declaration. The syntax in C# is slightly different then in Delphi.
tMyClass = class(tYourClass, YourInterface1, YourInterface2)
MyClass = class : YourClass, YourInterface, YourInterface2
-
Properties
To the user of the object a property behaves like any other field. In the actual implementation reading or writing the property will be done by an internal method. In this method the value can be checked or calculated. By only implementing only the getter or only the setter the property will be read or write only. In this example both the getter and the setter guard the property against a value less then 0.
type MyClass = class(tObject)
private
fMyProp : integer;
function GetMyProp: integer;
procedure SetMyProp(const Value: integer);
public
property MyProp : integer read GetMyProp write SetMyProp;
end;
function MyClass.GetMyProp: integer;
begin
result:= max(fMyProp,0);
end;
procedure MyClass.SetMyProp(const Value: integer);
begin
if Value >= 0 then
fMyProp:= Value;
end;In C# the value passed in the property setter is named value.
public class MyClass
{
private int myProp = 0;
public int MyProp
{
get { return Math.Max(myProp, 0); }
set { if (value > 0)
myProp = value; }
}
}
-
Events
Many Delphi components have notification properties. These will be notified when a specific event, like a click on the button, is fired. A method can be assigned to this notification, this method will be executed when the event is fired. In Delphi only one particular notification handling method can be assigned to the event-handler.
An event-handler in C# is based on delegate objects. Delegate objects hold the method to be executed when the click on the button does fire. The method is passed to the constructor of the delegate. The big difference with Delphi is that C# event handlers are multicast, they manage a dynamic collection of delegate objects and fire notifications to all the collection's members.
procedure MyEventHandler(sender : object)
begin
ShowMessage('You clicked the button');
end;
MyButton.OnClick:= MyEventHandler;
private void MyClick1(object sender, System.EventArgs e)
{
MessageBox.Show("You clicked the button");
}
private void MyClick2(object sender, System.EventArgs e)
{
MessageBox.Show("And I'll tell you twice");
}
MyButton.Click += new System.EventHandler(MyClick1);
MyButton.Click += new System.EventHandler(MyClick2);
-
Collections
The VCL has several classes which can be used as a base class for a collection. The namespace System.collections is a part of the .NET framework classes, it is a treasure-chest of classes and interfaces. The methods of a collection are defined in the IEnumerator interface, any class which implements this interface can be used as a collection. All collection classes in the FCL classes do implement IEnumerator, your own classes can do this as well. Items in such a collection can be enumerated with the foreach statement.
-
Runtime Type Information
A Delphi published property has RTTI, and can be queried on type information at runtime. In C# the type information of any object is available at runtime through the GetType() method of System.object, the base class of all .NET classes. This is named reflection in .NET.
-
Exceptions
Both languages use exceptions to handle errors. In Delphi an exception is said to be raised, in C# an Exception is said to be thrown. Both languages support the finally clause for cleanup code. In C# finally can be combined with catching an exception, to achieve this in Delphi two try's have to be nested
Try
Try
raise MyException.Create('Error occurred in Delphi');
except
on E: MyException
end;
finally
CleanUp;
end;try
{
throw New MyException("Error occurred in C#");
}
catch(MyException)
{
HandleMyException();
}
finally
{
CleanUp();
}
-
Single rooted classes
All classes in both languages stem from one single class, what tObject is to Delphi is System.object to C#.
To work with interfaced objects Delphi follows two tracks. In a later version of Delphi tInterfaced object was added to the VCL, a base class which implements the Iunknown interface and is refcounted. Since then tInterfacedObject has served as a base class for many components. Many classical COM interfaces are still implemented using tObject. This situation inevitably complicates the implementation of interfaced objects. Refcounting can be a problem with Delphi interfaced objects, the object will not be freed until the refcount reaches zero.
A .NET class can implement any interface, without doing any refcounting. All objects are being watched by the garbage collector (gc), as soon as an object is no longer referenced by anyone the gc will notice and clean up the object. This is the point where a gc really pays off.
-
Extendable class library
Both languages use a large class library, what the VCL is to Delphi is the framework class library, also known as the .net FCL, to C#.
The VCL was primarily aimed at encapsulating Windows complexity. Over the years an enormous amount of components built on the VCL has seen the light. They are provided by Borland and by many 3d party developers, together they provide solutions for almost every (in-) conceivable problem.
.NET was primarily aimed at bringing internet functionality to every corner of the development process. When your are interested in creating Windows forms with nice controls the VCL is still a clear winner. But the FCL is a library on which you can build, many 3d party Delphi component designers now supply .NET versions of their controls.
C# - .NET has
-
Garbage collector
Every object created in Delphi has to be freed again, a large part of this burden is taken care of by the owner of a tComponent descendant. When a tComponent is freed it will first free all tComponents owned by it.
In C# the freeing of objects is done by the .net garbage collector. Every object is being watched by the runtime system. The garbage collector, working continuously in the background, will free all objects which are no longer referenced by any piece of running code.
-
Type safety and custom type conversion
Type safety in C# assures that a variable is either of the declared type or is null. You can try any typecast in C# but both compiler and runtime system will perform extensive checks. An invalid typecast will throw an exception. A more gentle approach is the as keyword, also known in Delphi. The nice thing about as in C# is that a check on the type is performed. An invalid type will result in a null (nil) value.
MyType Myvar = Avar as MyType; if (MyVar == null) MessageBox.Show("This is not a MyType...");
As an extra option C# offers custom type conversions. This offers the possibility to explicitly program what will happen to (the members of) an object when it is typecast from one type to another. In Inside C# Tom Archer gives a very clear example. He takes two temperature types, Celsius and Fahrenheit. Typecasting a Celsius variable to a Fahrenheit variable will handle the conversion of the value on the fly.
Celcius c = new Celsius(0F);
Fahrenheit f = (Fahrenheit)c;The variable f will now hold a value of 32 (degrees Fahrenheit)
-
Static classes
A static class has only constant properties and static (class) methods. No objects will be created of a static class. It can have a static constructor to initialize the class, this will be executed when the program loads. A static class is a good place to house constants which will be used by several other classes.
class SharpGuids { /* Guid as string */ public const string intfguid = "E03D715B-A13F-4cff-92F1-0319ADB3DE5F"; public static readonly System.Guid idintf; /* Static constructor to init the Guid based on the string */ static SharpGuids() { idintf = new System.Guid(intfguid); } } -
Indexers
Any object in C# can be indexed if it was an array. Any resulting type and any type of the indexing value can be implemented by the writer of a class.
int cp = this["MyIndexer"]; MyObject o = ThatObject[12]; System.DateTime someTime = ThatObject[System.DateTime.Now];
An indexer is implemented by a writing an indexed property on this, the object itself.
public class MyClass
{
public object this[string myIndex]
{
get {return MyMethod(myIndex);}
}
} -
Attributes
Classes and methods can have metadata, properties which are of importance to the runtime environment. For an interface involved in COM it is important to know it's identifying GUID, for a method involved in a dispinterface it is important to know it's dispId, for a method involved in COM+ it is important to know the transaction characteristics of the methods. Delphi uses an attribute-like construction to bind a GUID to an interface :
IFileZapper = interface(IDispatch) ['{2E2FC5E0-5C0E-4C4F-8CC1-D9F6C5A92BA6}']C# uses attributes for all meta data.
[Guid(SharpGuids.intfguid), InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface IamSharp { [DispId(1)] void SayHi(string Anything); [DispId(2)] void SetOpacTo(int Percent); }Attributes in C# are objects themselves and can be based on user defined classes. Which give you as a developer a lot of power in defining and using your own custom metadata.
As all variables in .NET are objects, performing operations on them boils down to making methods calls. Methods can be overloaded, in .NET this is also possible for many of the basic operators like +, -, <, etc. This can be very useful in your own classes. Let's take an invoice class in which the + operator is overloaded
public class Invoice
{
public double Total;
public static Invoice operator+ (Invoice invoice1, Invoice invoice2)
{
Invoice NewInvoice = new Invoice();
NewInvoice.Total = invoice1.Total + invoice2.Total;
return NewInvoice;
}
}The method operator+ takes two Invoice objects as a parameter, constructs a new invoice and sets it's properties. After this declaration you can code:
Invoice anInvoice = new Invoice();
Invoice anotherInvoice = new Invoice();
anInvoice.Total = 1200;
anotherInvoice.Total = 2500;
textBox1.Text = "Total amount is " + (anInvoice + anotherInvoice).Total.ToString();The + operator applied on two Invoice object will result in another Invoice object with a total amount of 3700. The + operator applied on objects of any other type will result in a default addition, the + operator is overloaded for the Invoice class.
Where are we ?
C# and .NET can very well be compared to Delphi and the VCL. The fact that the first is a clean start based on yesterdays lessons and today's needs make it a very tempting development environment. Working with C# in Visual Studio.net has been a very pleasing experience to me, it shows a stable and powerful implementation of all ideas and will keep the pleasure in programming for quite some time.

浙公网安备 33010602011771号