C# for Delphi developers

原创 2004年09月06日 10:10:00

C# for Delphi developers1.0 022202

Foreword

This document is intended to be a reference for those Delphi programmers who want to learn C#. It explains how to translate your existing knowledge to C#. I hope you could forgive my mistakes, I'm not an english speaker and you are invited to send me any bugs or incorrect information you find at studio@qadram.com, I will fix it as soon as possible.

Introduction

C# is a component-oriented language, in the same way Delphi and C++Builder are, but it goes a bit beyond this concept. This is not a C# manual, after reading this manual, you must buy some C# book to learn all the new features C# have and Delphi not. Some descriptions and differences are explained “roughly”, is just an introduction to C# for Delphi Developers, it's not an in-depth C# manual.


Some things this manual doesn't cover

  • How to compile or execute C# programs
  • Attributes
  • Interfaces
  • Nested Classes
  • Versioning
  • C# language syntax
  • Conversions
  • Arrays
  • Indexers
  • Enumerators
  • User-defined Conversions
  • Operator Overloading
  • COM
  • .NET Framework
  • XML

Is this manual for you?

If you are a Delphi developer who wants to know how to translate your knowledge to C#, this is the first document you might want to read. It introduces you in the C# by explaining how to do the things you already do with Delphi. This manual is only about the language C#, not the .NET platform nor the Visual Studio .NET IDE, nor components.

About the Author

José León currently works as web developer for a company in Spain, if you want to know more about him, just go to http://www.qadram.com

QuickStart

Look at this source code:

using System;
class Hello
{
    public static void Main(string[] args)
    {
        Console.WriteLine(“Hello, from C#”);
        for (int arg=0; arg
        {
            Console.WriteLine(“Arg {0}:{1}”, arg, args[arg]);
        }
    }
}

When the program loads, the Main function is executed, it writes a string to the console and iterates through the parameters to dump the number of parameters and its value.

Main differences:

  • Using instead of Uses to add external functionallity
  • There's no code outside a class, there are no global functions in C#
  • The implementation of a method is tied to its declaration

DataTypes

The following table shows the correlation between Delphi data types and C# data types:

Delphi

C#

byte

byte

shortint

sbyte

smallint

short

word

ushort

integer

int

cardinal, longword

uint

int64

long

ulong

single

float

double

double

currency

decimal

string

string

char

char

boolean

bool

The main diference between Delphi datatypes and C# datatypes is not its name, in C#, datatypes are derived from the base class object, so it's possible to write sentences like this:

Console.WriteLine(“Value is:{0}”, 3.ToString());

Which is not possible in Delphi, this technique is called boxing.

Exception Handling

I can't see many differences between C# Exception Handling and Delphi's one, it's another syntax but nothing more:

Try .. Except .. End

try
{
    int j=22 / Zero;
}
catch (Exception e)
{
    Console.WriteLine(“Exception “+e.Message);
}

If you want to catch different kind of exceptions you just only need to write several catch blocks. The ancestor of all exceptions is the Exception class, the same as Delphi.

try
{
    int j=22 / Zero;
}
catch (DivideByZeroException e)
{
    Console.WriteLine(“Exception “+e.Message);
}
catch (Exception e)
{
    Console.WriteLine(“Exception “+e.Message);
}

The catch block that catches the DivideByZeroException is the more specific match.

Try..Finally..End

C# also supports Try..Finally using similar syntax to Delphi

FileStream f = new FileStream(“data.txt”, FileMode.Open);
try
{
    //perform operations with the file 
}
finally
{
    f.Close();
}

There's another option which is equivalent to this Delphi code:

try
    try
        except
            // manage here your exceptions
        end
finally
    // perform some cleanup here
end;

In C# you can write:

try
{
}
catch (Exception e)
{
    Console.WriteLine(“Exception “+e.Message);
}
finally
{
    // perform some cleanup here
}

In Delphi, you should destroy any object you create or free any resource you allocate. The primary use of exception handling is to ensure everything is destroyed and freed. Because C# has Garbage Collection, you don't need to worry about this, you will use exception handling only to ensure the proper finalization of operations, so this improves the efficiency of the code.

Classes

C# classes work like Delphi, you don't need to learn new OO concepts, the main difference, at source code level, is the code is written just below the prototype of a function.

class MyClass
{
    public int myFunction()
    {
        return(5);
    }
}

Also you must specify the visibility of each member of a class (field, method, etc), because they are private by default. For example:

class MyInteger
{
    int integer=0;
}

If you create an instance of MyInteger and you use the integer field of the MyInteger class, this will produce an error because is private:

class TestPrivate
{
    public static void Main()
    {
        MyInteger mi = new MyInteger();
        Console.WriteLine(“{0}”, mi.integer);
    }
}

This will produce an error, you must specify public when declaring a member visible to other classes.

In the previous sample, you don't need to destroy the mi variable, the garbage collector does it for you. In Delphi you can use self inside a class to refer to the current instance, in C# use this instead self.

Constructors

Constructors are not labeled with the constructor reserved word, in C#, constructors are member functions with the same name of the class:

class Point
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public int x;
    public int y;
}

ref and out Parameters

To get several values at once from a function, in Delphi you use var, to indicate a reference parameter, the function can modify the parameter and the caller will know the new value.

There are two ways to do this in C#, you can use ref or out. The difference is the compiler won't allow to pass uninitialized variables as ref parameters.

class Point
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    public void GetPoint(ref int x, ref int y)
    {
        x = this.x;
        y = this.y;
    }     
    int x;
    int y;
}

To call the GetPoint member of the Point class, you should call it as:

int x=0;
int y=0;
Apoint.GetPoint(ref x, ref y);

If you forget to initialize the parameters, the compiler will generate an error, if you don't want this, just change ref by out.

Overloading

To declare several methods in a class with the same name and different parameter list, you don't need to mark them as overload. The compiler chooses which to call by matching the parameters in the call to the parameters declared for the function.

Inheritance

To create derived classes from base classes, you can do it this way:

class BaseClass
{
    public BaseClass()
    {
        //Do something
    }
}
class DerivedClass: BaseClass
{
    public DerivedClass()
    {
    }
}

One big difference is constructors cannot be inherited, so you must ask, How can I call the base class constructor?. In Delphi, you can use the inherited reserved word, in C# you use the base syntax. Look at the following sample:

class BaseClass
{
    public BaseClass(string param, int value)
    {
        this.param = param;
        this.value = value;               
    }
    string param;
    int value;
}
class DerivedClass: BaseClass
{
    public DerivedClass(string param,int value): base (param, value)
    {
    }
}

Using the base syntax, you call the constructor from the base class with the parameters of the constructor of the derived class, if you omit the base syntax, the base class constructor will be called with no parameters.

Virtual Functions

To declare virtual functions and override them in derived classes, the syntax is very similar to Delphi, to allow a function be overritten in a derived class, you must declare it as virtual, and in a derived class to override a function from the base class, you must declare it as override.

class BaseClass
{
    public BaseClass(string param, int value)
    {
        this.param = param;
        this.value = value;               
    }
    virtual public getValue()
    {
        return(value);
    }
    string param;
    int value;
}
class DerivedClass: BaseClass
{
    public DerivedClass(string param,int value): base (param, value)
    {
    }
    override public getValue()
    {
        return(value*5);
    }
}

Abstract Classes

If you want to force a derived class to override one or more members, you can declare them as abstract, this means you don't need to create an implementation for that member in the base class and you are forcing a derived class to implement it.

There are several differences between C# and Delphi, in Delphi, you can declare an abstract member, but you are not forced to implement it on a derived class, you will get just a warning, and in runtime, if you call this member, you will get an abstract error.

In C# you are forced to implement an abstract member in a derived class, if you don't do it, the program won't compile. Also, if you declare an abstract member on a class, you must declare the class as abstract and you can't create instances of that class because it contains missing functionality. Look at this example:

abstract class BaseClass
{
    public BaseClass(string param, int value)
    {
        this.param = param;
        this.value = value;               
    }
    abstract public getValue();
    string param;
    int value;
}
class DerivedClass: BaseClass
{
    public DerivedClass(string param,int value): base (param, value)
    {
    }
    override public getValue()
    {
        return(value*5);
    }
}

You cannot create instances of BaseClass, and if you don't override the member getValue, the program won't compile.

Sealed Classes

There's another feature I think Delphi doesn't have, is called Sealed Classes, and is a way to prevent derivation from a base class.

This program will produce an error:

sealed class MyClass
{
    MyClass()
    {
    }
}
class derived: MyClass
{
}

Class Member Accesibility

In Delphi, you can declare several classes in the same unit, let's supose class A and class B, if you declare a private member on class A, you can access that member inside class B.

TClassA=class
private
    a: integer;
end;
TClassB=class
public
    procedure hello;
end;
implementation
procedure TClassB.hello;
var
    classA:TClassA;
begin
    classA:=TClassA.create;
    classA.a:=5;
end;

In C# this is not the default behaviour, to achieve this behaviour, you must declare the member as internal.

public class ClassA
{
   internal int a;
}
public class ClassB
{
   public void hello()
   {
       ClassA ca=new ClassA();
       ca.a=5;
    }
}

In C# there's a concept called assembly, which can be considered as a package for Delphi developers. Declaring internal a member of a class, you can access that member from any class inside the same assembly, but not from a class outside the assembly.

The internal syntax can also be used with classes, because if you don't specify a visibility for a class (i.e. public), the class could not be used outside the assembly. To create a class that could be used “only” inside the assembly, declare it as internal.

Destruction

There are important differences between Delphi's memory management and C#'s one. In Delphi you should take care to destroy each and every object you create to avoid memory leaks, but in C# you don't have to worry about that.

Strictly speaking, C# doesn't have destructors, at least not in the way Delphi does. In C# a destructor is like a finalizer and is called by the Garbage Collector when the object is collected. So the destructor is not so useful as in Delphi, we must provide another method for the user of our object to perform cleanup.

Static Fields and Member Functions

In Delphi, the easiest way (and the only way I know) to have a field shared by several instances of the same class, is declare it outside the class. Declare it as a global variable or a local variable under the implementation section. But C# gives us another way to do it, you can declare a member of a class as static, this means, it's value will be shared among all the instances of the same class, and it's accessed through the name of the class, instead the name of the instance.

class StaticClass
{
    public StaticClass()
    {
        instances++;
    }
    public static int instances=0;
}
class StaticTest
{
    public static void Main()
    {
        StaticClass sc=new StaticClass();
        Console.WriteLine(StaticClass.instances);
        StaticClass sc2=new StaticClass();
        Console.WriteLine(StaticClass.instances);
    }
}

Also, you can declare methods of the class as static, and in that case, works in the same way as class methods in Delphi.

Constants

To declare a constant, you just need to declare it with the const modifier and assign it a value, but you must include the type of the value.

public const int value=33;

But there is another powerful feature to allow us initialize constants to a value we don't know at compile time. This feature is called readonly. If we use the readonly modifier instead the const modifier when declaring a constant, we are allowed to set its value only at the constructor of a class. If we try to modify it in another location, we will get an error.

class ReadOnlyClass
{
    public ReadOnlyClass(int value)
    {
        this.value=value;
    }
    public readonly int value;
}

In this way, value could not be modified in any part of the program except in its constructor. Works very similar to readonly properties, but in my opinion, the syntax is much more clear.

Variable Parameters

In Delphi, a function can take a variable number of parameters, C# is able to do this in a fancier way because in C# everything is an object, take a look at this code:

class VariableParameters
{
    public void Write(string line, params object[] args)
    {
        for (int index=0; index < args.GetLength(0); index++)
        {
            Console.WriteLine(args[index].ToString());
        }
    }
}
class TestVariableParameters
{
    public static void Main()
    {
        VariableParameters vp=new VariableParameters();
        vp.Write(“Hello ”, “World!”);
        vp.Write(“Params:”, “a”, “b”, 12, 15);
        object[] arr=new object[4];
        arr[0] = “These”;
        arr[1] = “are”;
        arr[2] = 4;
        arr[3] = “params”;
        vp.Write(“Params:”,arr);
    }
}

If we use the params keyword on a parameter of a function, we are able to pass as many parameters as we want to that function. The compiler internally converts that list of parameters to an array and calls the function with that array, the same as in the third call.

Structs

Structs are really different to Dephi records, because they inherit from object, act like objects but with a few restrictions, they can't inherit from any other type and other classes can't inherit from them. It's really strange to see a struct that has methods, but we must think that is an object, in the same way an integer or a string.

So we can write this code:

struct Rect
{
    public Rect(int x1, int y1, int x2, int y2)
    {
        this.x1 =x1;
        this.y1 =y1;
        this.x2 =x2;
        this.y2 =y2;
    }
    public override string ToString()
    {
        return(String.Format(“({0},{1})-({2},{3})”,x1,y1,x2,y2);
    }
    public int x1;
    public int y1;
    public int x2;
    public int y2;
}
class TestRect
{
    public static void Main()
    {
        Rect bounds = new Rect(100,100,200,200);
        Console.WriteLine(“Bounds: {0}”, bounds);
    }
}

This code is so strange to a Delphi developer, because first, declares methods in a struct, second, a method uses the override reserved word, and third, the struct is written to the console.

We can declare methods in a struct because is an object, because is an object we can override certain functions of the object ancestor (ToString is available for all objects), and the ToString method is called for each an every object is passed to the WriteLine function.

Sentences

I'm not going to write here all the sentences of Delphi and its equivalent in C# because most part are the same in C++, you should read another tutorials/books about that.

Switch

Switch is equivalent to Case in Delphi, but has a total different syntax:

switch (condition)
{
    case 1: 
    case 2:
                    //Do something here for case 1 and case 2
            break;
    case 3:
                    //Do something here for case 3
            break;
    default:
            //Handle here the default case
}

Works very similar to C++ but it requires a break at the end of each case block, this is a common mistake in C++, so the compiler gives an error if there's no break.

Foreach

C# incorporates a feature to make iterations simpler, is called foreach, and allows to iterate easily through an array (also allows another types, but it's outside of the scope of this manual).

Look this code:

public static void ForeachTest(ArrayList myArray)
{
    for (int i=0; i < myArray.Count ; i++)
    {
        MyObject current=(MyObject) myArray[i];
        Console.WriteLine(“Item: {0}”, current);
    }
}

With foreach, you can write the previous code as follows:

public static void ForeachTest(ArrayList myArray)
{
    foreach(MyObject current in myArray)
    {
        Console.WriteLine(“Item: {0}”, current);
    }
}

Return

Return, returns to the calling function and optionally returns a value, is like assign a value to result and make an exit.

As and Is

These operators can be used in the same way in Delphi, you can know the type of an object/interface and force it to be of a specified type.

Strings

Because strings are objects too, you can apply to string variables several methods to compare them, make operations, iterate, etc.

This is a brief list of all the methods that supports a string:

Compare()

CompareOrdinal()

CompareTo()

EndsWith()

StartsWith()

IndexOf()

LastIndexOf()

Concat()

CopyTo()

Insert()

Join()

PadLeft()

PadRight()

Remove()

Replace()

Split()

Substrng()

ToLower()

ToUpper()

Trim()

TrimEnd()

TrimStart()

To make string processing easier, there's a class called StringBuilder which can be useful to make such operations.

Regular Expressions

To deal with regular expressions, there's a class called Regex which handles all this stuff. You just create a new object of Regex class and as parameter you use the regular expresion, then you can use the several methods provided with this class to perform operations to strings.

Properties

The syntax for properties is totally different to Delphi's one. In fact, if you don't pay attention, you can't see is a property, because it looks like a method.

The concept is the same, a property is a way to get the value from a field of an object, or to set that value, a property itself it's not a value. Look that code:

class Test
{
   private string name;
   public string Name
   {
       get
       {
           return name;
       }
       set
       {
           name = value;
       }
   }
}

Personally, I prefer the way Delphi declares properties, for example, you see the value parameter of the set accessor, but here is hidden. If a property has only a get accessor (getter), it's a read-only property, and if it has only a set accessor (setter) it's a write-only property.

Delegates

In Delphi you can write:

type
   TWakeUpEvent=procedure(Sender:Tobject; When:integer) of object;

You are creating a procedural type, then you can declare variables of that type, and this mechanism is needed when declaring events.

In C#, the concept is called delegate, in C#, the previous line can be written as follows:

public delegate void TWakeUpEvent(object Sender, int When);

Because in C#, all the code must be written inside a class, you don't need to specify of object. After that declaration you can write:

class WakeMe
{
   public delegate void TWakeUpEvent(object Sender, int When);
   public void WakeMeUp(TWakeUpEvent wakemeup)
    {
       wakemeup(anobject, 500);
   }
}

But this has some drawbacks, because you must create an instance of the delegate, passing as an argument, the function you want to be called.

class Test
{
   public static void Notified(object Sender, int When)
   {
       Console.WriteLine(“When: {0} “, When);
   }
   public static void Main()
   {
       WakeMe wm=new WakeMe();
       WakeMe.TWakeUpEvent ev=new WakeMe.TWakeUpEvent(Notified);
       WakeMe.WakeMeUp(ev);
   }
}

Events

Events in C# are based in delegates, and there's a design convention to design the delegates to take always two parameters, the first is the object that fired the event and the second parameter is an object that contains the information about the event, this object is always derived from EventArgs. In Delphi, the convention is to define an event with Sender as first parameter and the rest of information in separate parameters.

class MyEventArgs: EventArgs
{
    public MyEventArgs(string name, string value)
    {
        this.name=name;
        this.value=value;
    }
    public string Name
    {
        get
        {
            return(name);
        }
    }
    public string Value
    {
        get
        {
            return(value);
        }
    }
    string name;
    string value;
}
class MyNotify
{
    public delegate void MyEventHandler(object sender, MyEventArgs e);
    public event MyEventHandler OnMyEventHandler;
    protected void OnMyEvent(MyEventArgs e)
    {
        if (OnMyEventHandler != null) OnMyEventHandler(this, e);
    }
    public void NotifyEvent(string name, string value)
    {
        MyEventArgs e = new MyEventArgs(name, value);
        OnMyEvent(e);     
    }
}
class MyWatcher
{
    public MyWatcher(MyNotify myNotify)
    {
        this.myNotify = myNotify;
        myNotify.OnMyEvent += new MyNotify.MyEventHandler(MyFunction);
    }
    void MyFunction(object sender, MyEventArgs e)
    {
        Console.WriteLine(“New Mail:{0}
{1}”, e.Name, e.Value);
    }
    MyNotify myNotify;
}
class Test
{
    public static void Main()
    {
        MyNotify myNotify = new MyNotify();
        MyWatcher myWatcher = new MyWatcher(myNotify);
        myNotify.NotifyEvent(“this is a name”, “This is a value”);
    }
}

The first time I saw this code, I started to think that C# is not as good with events as Delphi, but let's look at the line:

   myNotify.OnMyEvent += new MyNotify.MyEventHandler(MyFunction);

This line assigns a handler to the event, but then, Why +=, instead a simple assignment? In C#, instead assign a pointer to an event, you are “subscribing” to a notification list, that means, several functions can be notified when an event is fired without do anything special. This feature is really useful. In C#, this feature is called multicast, and there are two things you must be aware, the order in which delegates are called is not defined, and if one delegate throws an exception, maybe, the other delegates are not called.

Conclusion

If you were a C++ developer who changed to Delphi due RAD capabilities (this is my story ;-) ), you will feel very comfortable with C#, it gives you the best of C++ with a Delphi flavour.

F# for C# Developers

  • 2013年08月03日 11:02
  • 5.01MB
  • 下载

Desktop Window Manager ——Delphi开发学习 .

原文地址:http://msdn.microsoft.com/en-us/library/windows/desktop/aa969540(v=vs.85).aspx   桌面合成功能,在Wind...
  • kent19900125
  • kent19900125
  • 2013-05-10 11:04:22
  • 2186

Why do Java programmers wear glasses?

面试官:Why do Java programmers wear glasses?        程序员:Because they do not C#.        面试官:你被录取了....
  • stpeace
  • stpeace
  • 2012-11-27 22:36:15
  • 4498

Hadoop for .NET Developers(八):以编程方式将数据加载到HDFS

在本系列的最后一篇博文中,我们讨论了如何手动将数据加载到集群。虽然这对偶尔的需求是适用的,但是编程访问是更为方便,更为典型的方案。为了实现这一点,Hadoop在HTTP端口50070上提供了一个RES...
  • WuLex
  • WuLex
  • 2017-09-23 10:59:08
  • 620

#Eclipse IDE for Java EE Developers 的下载及初步使用

Eclipse IDE for Java EE Developers 的下载及初步使用最近想用Eclipse写个小项目,突然发现以前的Eclipse上不支持tomcat8,上网找了一下方法,比较麻烦,...
  • qq_23091073
  • qq_23091073
  • 2017-05-02 19:01:40
  • 3202

Docker and Kubernetes for Java Developers

  • 2017年10月08日 20:54
  • 5.3MB
  • 下载

Software Architecture for Developers

  • 2015年07月25日 08:01
  • 26KB
  • 下载

《Kotlin for android developers》中文版翻译

转载地址:https://github.com/wangjiegulu/kotlin-for-android-developers-zh
  • lijinhua7602
  • lijinhua7602
  • 2016-03-22 11:40:03
  • 3260

eclipseIDE for javaee developers 开发环境搭建详解图文

使用eclipse真的有年头了,相信java程序员没有不知道它的,最近在给团队中新来的应届生做指导,专门讲解了一下Eclipse开发环境的搭建过程,一是帮助他们尽快的熟悉IDE的使用,二也是保证团队开...
  • i10630226
  • i10630226
  • 2015-08-15 19:41:13
  • 1280

Augmented Reality for Developers azw3

  • 2017年10月13日 16:49
  • 12.11MB
  • 下载
收藏助手
不良信息举报
您举报文章:C# for Delphi developers
举报原因:
原因补充:

(最多只允许输入30个字)