【问题标题】:How to restrict a type in C#如何在 C# 中限制类型
【发布时间】:2015-03-05 14:19:46
【问题描述】:

在 Delphi 中我可以这样做:

type
    TAnimal = class
        Name: String;
        constructor Create(const Name: String);
    end;

    TAnimalType = class of TAnimal;

    TElephant = class(TAnimal);

    TEmu = class(TAnimal);

    THovercraft = class;

implementation

var
    Zoo: TObjectList;

procedure PopulateZoo;
begin
    Zoo := TObjectList.Create(True);
    AddToZoo(TElephant, "Ellie");
    AddToZoo(TEmu, "Erik");
    AddToZoo(THovercraft, "Helen"); //Compiler error here
end;

procedure AddToZoo(AnimalType: TAnimalType; Name: String)
var
    Animal: TAnimal;
begin
    Animal := AnimalType.Create(Name);
    Zoo.Add(Animal);
end;

代码编译失败,因为 THovercraft 没有从 TAnimal 继承。如果AddToZoo(THovercraft); 被删除,那么代码将被编译。运行它并调用 PopulateZoo 将创建一个包含 TElephant 实例和 TEmu 实例的列表。

如何在 C# 中创建这种行为?我想要的主要功能是

1) 编译器强制 AddToZoo 仅将 TAnimal 及其后代作为其第一个参数,并且

2) 在 AddToZoo 中我可以轻松调用 TAnimal 中声明的构造函数。

附注这段代码被简化以演示我的问题。

【问题讨论】:

  • 向我们展示您已经拥有的 C# 代码(没有您要求的限制)。
  • C# 没有元类的概念,因此没有编译时方法可以实现完全相同。在 C# 中也无法使用元类进行实例化。所以没有像AnimalType.Create 这样的东西。顺便说一句,它需要一个虚拟构造函数。您应该停止尝试使用文字端口,而是使用 C# 提供的功能编写代码。
  • @DavidHeffernan “使用 C# 提供的功能编写代码” - 这就是我寻求帮助的原因。我不是要一个字面端口。
  • @cja C# 中没有元类。那么你需要一个不同的解决方案来解决你的问题。
  • @cja 我不知道你的问题是什么。

标签: c# delphi oop inheritance types


【解决方案1】:

您想要的在 C# 中“几乎”是可能的。您可以使用 genericstype constraints 来获得该行为:

public class Animal
{
    public string Name { get; set; }
}

public class Elephant : Animal {}
public class Emu : Animal {}
public class Hovecraft {}

public class Test
{
    private List<Animal> animals = new List<Animal>();

    private void AddToZoo<T>(string name)
        where T : Animal, new()
    {
        var animal = new T();
        animal.Name = name;
        this.animals.Add(animal);
    }

    public void PopulateZoo()
    {
        this.AddToZoo<Elephant>("Ellie");
        this.AddToZoo<Enu>("Erik");
        this.AddToZoo<Hovercraft>("Whatever");  // Compiler error
    }
}

在 C# 中你不能做的是在没有默认构造函数的情况下使用类型约束;使用带有类型约束requires you to use a default constructornew 关键字。您可以通过向基类添加 Initialize 方法来解决此问题:

public class Animal
{
    public string Name { get; set; }
    public void Initialize(string name) { this.Name = name; }
}

并像这样使用它:

var animal = new T();
animal.Initialize(name);
this.animals.Add(animal);

如果您不想使用类型约束,您仍然可以在运行时传递 Type 对象并使用 Activator 使用反射创建您的实例:

var animalType = typeof(Emu);

if (!typeof(Animal).IsAssignableFrom(animalType)) throw new Exception("Bummer.");
var animal = (Animal)Activator.CreateInstance(animalType);
// use animal

【讨论】:

  • 能否把带Name参数的构造函数添加到Animal类中,然后在AddToZoo中调用?
  • 这样更好,但强制在编译时确定类型。我知道它们在 Q 中,但在任何现实世界的场景中,类型都可能在运行时确定。我预计提问者尚未意识到这种细微差别。
  • @cja 那是不可能的。您只能将 TAnimal 约束为具有无参数构造函数(在方法签名中使用 new() 约束)。您不能强制 TAnimal 拥有任意构造函数。
  • @DavidHeffernan 为什么在此答案中将 TAnimal 重命名为 T? (您应该在编辑时添加编辑摘要。)
  • @DavidHeffernan 为什么不用 TAnimal?
【解决方案2】:

我唯一能想到的就是使用泛型 (Generics (C# Programming Guide))。使用类型参数约束,您可以模仿您提到的 Delphi 的类型限制。

class TAnimal {
    public TAnimal(string Name) { this.Name = Name; }
    public string Name { get; set; }
}

// Constructors cannot be inherited, so we have to implement it (calling
// the base class constructor) on each one of the derived classes.

class TElephant : TAnimal { public TElephant(string Name) : base(Name) { } }
class TEmu : TAnimal { public TEmu(string Name) : base(Name) { } }
class THovercraft { }

IList<object> Zoo = null;

void PopulateZoo() {
    Zoo = new List<object>();
    AddToZoo<TElephant>("Ellie");
    AddToZoo<TEmu>("Erik");
    AddToZoo<THovercraft>();
}

// Here, we specify the constraint [class TAnimal] for the type parameter T.

void AddToZoo<T>(string Name = null) where T: TAnimal {
    T Animal = (T) Activator.CreateInstance(typeof(T), Name);
    Zoo.Add(Animal);
}

如果你尝试编译它,你会得到错误信息:

类型“UserQuery.THovercraft”不能用作泛型类型或方法“UserQuery.AddToZoo(string)”中的类型参数“T”。没有从“UserQuery.THovercraft”到“UserQuery.TAnimal”的隐式引用转换。

使用LINQPad 版本 4 对其进行了测试。

注意: 没注意到弗兰克用同样的方法回答。

【讨论】:

    猜你喜欢
    • 2011-10-16
    • 2010-12-30
    • 2017-08-10
    • 2017-10-22
    • 2011-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多