C#中的Delphi类

10

我知道这个问题之前已经被问过了,但是我还没有看到一个简短明了的答案,所以我希望他们不会移除这个问题,并且我现在能够得到一个清晰的答案:

我目前正在使用C# 5.0; .NET 4.5;VS 2012。虽然我用C#做了很多工作,但我主要是Delphi开发者。

在Delphi中,我编写了数百个类工厂,它们使用以下设计(这里大大简化):

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.

现在:在 C# 中如何做类似于这样的事情?!似乎在 C# 中没有“class of”类型。我有什么遗漏吗?如何简单而优雅地实现此类型的类工厂在 C# 中?Python 中也可以实现此设计 - 为什么 C# 会更差呢?!


TMyClass1和TMyClass2是TMyClass的子类吗? - Martheen
@martheen 是的,这是由 TMyClass 类强制执行的。 - David Heffernan
C# 还缺少这个吗? - cja
4个回答

9

您可以使用类型:

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(....);
}

但我认为你应该使用通用方法:

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

这里有一种参数化构造的方法:

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;
}

当然, 与第一个例子一样,如果无法找到匹配的构造函数,这将抛出一个nullpointerexception...


谢谢 - 我也会查看这个。这里还有另一个答案也使用了Invoke(),我也在网上看到过在工厂方法中使用它的代码。 - Vector
1
请注意,这比Delphi中的功能稍弱,因为您无法强制/依赖于子类具有特定的构造函数。(在C#中,除了无参数的构造函数外,没有通用约束条件适用于构造函数。) - O. R. Mapper
2
@O.R.Mapper:Delphi的泛型构造函数约束也适用于无参构造函数。只有在使用元类时,您才可以使用更具体的构造函数,这些构造函数在从TObject到定义元类的类的类层次结构中定义。 - Marjan Venema
1
@MarjanVenema:确切地说,我是指C#在元类方面略逊于Delphi。 - O. R. Mapper
@Stig-Rune Skansgård - 我在Delphi中也不需要基于枚举的键字典,但是代码是运行在一个接收XML请求的web服务器上的,这些请求会被发送给工厂,因此枚举和词典可以使其变得更容易 - 可以轻松地在XML中表示枚举并将XML枚举直接发送到工厂。当然有时我会使用参数化构造函数。 - Vector
显示剩余2条评论

4
    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);
    }

5
@Mikey: 这是一个lambda表达式 - O. R. Mapper
1
在C#中没有一个名为MyEnum的数组,这不是很遗憾吗? - David Heffernan
1
需要使用Invoke吗,还是可以直接写dicte - David Heffernan
1
@Mikey编辑了我的答案。如果需要,可以将object[]更改为更具体的示例。 - Martheen
1
@Martheen:明白这不是一个直接的答案。但使用Func和Lambda很优雅。我想在2.0中你必须声明委托类型等。但对我来说,Delphi仍然是最好的选择,现在他们引入了泛型和匿名方法。Pascal几乎强制你编写结构化、表达性强的代码,并具有一些很棒的功能。没有Lambda和Linq,但你仍然拥有最大的灵活性——可以编写非常低级别或非常高度抽象的代码,支持函数式编程,与C#相同的面向对象模型。最大的缺点是缺乏自动垃圾回收,但也有解决方法。 - Vector
显示剩余11条评论

1
C#语言不支持元类。因此,您需要以其他方式实现工厂。一种方法是在枚举上使用switch语句:
switch (aEnum)
{
     case myEnum1:
         return new MyClass1();
     case myEnum2:
         return new MyClass2();
     .....
}

另一种常用的选项是使用反射来编写代码,这样可以让你更接近于自己习惯的方式。
还有一种选择是用返回新对象实例的委托字典替换类字典。使用lambda语法,该选项会产生非常干净的代码。
反射的缺点是放弃了编译时类型安全性。因此,虽然基于反射的方法可能最接近问题中的Delphi代码,但这不是我个人会选择的路线。
与其试图将Delphi解决方案塞入不希望采用该方法的语言中,我建议您寻找最符合C#语言习惯的解决方案。从类工厂开始进行网络搜索。

+1 - 谢谢。是的,我知道关于 switch {case:} 的内容 - Delphi 也有这个用于枚举类型的功能,正如您所知道的那样。"从类工厂开始进行网络搜索" - 再次感谢您。现在请恕我直言,请再次阅读我的问题。 :-) - Vector
1
我认为我理解了这个问题。我的意思是,你会在网上找到各种各样的C#工厂示例。看一下它们,选择你最喜欢的方法。 - David Heffernan
2
你得到了明确的答案。一个简单的陈述是没有类的等价物。我已经概述了三种可能的工厂实现方式。我认为一些网络搜索会产生更多的想法。你可能已经这样做了,只是想知道是否可以使用元类来实现。嗯,不完全是。最接近元类的方法是使用反射。 - David Heffernan
1
但是什么呢?我只是说有很多方法可以制造工厂。很难推荐一个。你需要做出个人选择。使用网络搜索将为您提供一些超出此处范围的额外想法。有什么害处吗?虽然我个人无法想到除我概述之外的任何想法,但我敢打赌还有一些! - David Heffernan
1
这就是我不同意的地方。与你的代码最接近的是基于反射的方法。但我会选择使用委托字典。我认为您需要根据语言编程,而不是尝试将一种语言的解决方案应用于它不适合的语言。个人而言,我不喜欢反射方法。除非没有干净的替代方案,否则我会避免使用反射。这就是我最后一段的观点。 - David Heffernan
显示剩余6条评论

1

1
在Delphi中,“Class of”是一个metaClass——一种可以通用地表示该基类的所有后代并根据所表示的后代类与多态性一起工作的类型。与静态类无关。 - Vector

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接