Delphi Class of in C#

2019-02-06 15:34发布

问题:

I know this question has been asked before, but I have yet to see a short, clear answer, so I'm hoping they won't remove this question and I will now get a clear answer:

I am currently working in C# 5.0; .NET 4.5; VS 2012. I am mostly a Delphi guy although I've done lots with C#.

In Delphi I have written hundreds of class factories that use the following sort of design (MUCH SIMPLIFIED HERE):

unit uFactory;

interface


type

    TClassofMyClass = class of TMyClass;
    TFactoryDict = TDictionary<TMyEnum, TClassofMyClass>;

var fDict:TFactoryDict;

implementation  

procedure initDict;
begin

    fDict:=TFactoryDict.create;
    fDict.add(myEnum1, TMyClass1);
    fDict.add(myEnum2, TMyClass2);
    fDict.add(myEnum3, TMyClass3);

end;


function Factory(const aEnum: TMyEnum): TMyClass;

var

    ClassofMyClass: TClassofMyClass;

begin

    if fDict.TryGetValue(aEnum, ClassofMyClass) then

    result := ClassofMyClass.Create(aParam);

end;

end.

Now: HOW do I do something like this in C#?! Seems there is NO 'class of ' type in C#. Am I missing something? How can I implement this type of class factory simply and elegantly in C#? This design can be implemented in Python as well - why should C# be worse?!

回答1:

You can use Type:

Dictionary<ClassEnum, Type> TypeDictionary = new Dictionary<ClassEnum, Type>();

public void InitDictionary()
{
    TypeDictionary.Add(ClassEnum.FirstClass, typeof(FirstClass));
    //etc...
}

public object Factory(ClassEnum type)
{
    if (!TypeDictionary.ContainsKey(type))
        return null;

    var constructor = TypeDictionary[type].GetConstructor(....);
    return constructor.Invoke(....);
}

But I think you should use a generic method:

public T Factory<T>(): where T is MyBaseClass
{
    var type = typeof(T);
    var constructor = type.GetConstructor(....);
    return constructor.Invoke(....) as T;
}

Here is a variety for parameterized construction:

public T Factory<T>(params object[] args): where T is MyBaseClass
{
    var argList = new List<object>(args);
    var type = typeof(T);
    var argtypes = argList.Select(o => o.GetType()).ToArray();
    var constructor = type.GetConstructor(argtypes);
    return constructor.Invoke(args) as T;
}

And of course; As with the first example, this will throw a nullpointerexception if it can't find a matching constructor...



回答2:

    class Potato
    {
    }

    class Potato1 : Potato
    {
        public Potato1(object[] param) { }
    }

    class Potato2 : Potato
    {
        public Potato2(object[] param);
    }

    enum MyEnum
    {
        E1, E2
    }

    Dictionary<MyEnum, Func<object[], Potato>> dict = new Dictionary<MyEnum, Func<object[], Potato>>(){
            {MyEnum.E1,(d)=>new Potato1(d)},
            {MyEnum.E2,(d)=>new Potato2(d)}
        };

    Potato Factory(MyEnum e, object[] param)
    {
        return dict[e](param);
    }


回答3:

If i understood you correct you want to have a reference to a static class. This is not possible in c#.

just one example of factory method implementation: http://www.codeproject.com/Tips/328826/implementing-Factory-Method-in-Csharp



回答4:

The C# language does not support meta classes.

So you'll have to implement your factory in another way. One way is to use a switch statement on an enum:

switch (aEnum)
{
     case myEnum1:
         return new MyClass1();
     case myEnum2:
         return new MyClass2();
     .....
}

Another commonly used option is to do it with reflection which would allow you to write code closer to what you are used to doing.

And yet another option is to replace your dictionary of classes with a dictionary of delegates that return a new instance of your object. With lambda syntax that option yields very clean code.

The disadvantage of reflection is that you give up compile time type safety. So whilst the reflection based approach is probably closest to the Delphi code in the question, it's not the route that I personally would choose.

Rather than trying to shoe horn your Delphi solution into a language that does not want that approach, I suggest you look for the most idiomatic C# solution. Start with a web search for class factory.