【问题标题】:Can I hold a List<> of Object Types of the same descendant?我可以持有相同后代的对象类型的 List<> 吗?
【发布时间】:2012-09-26 15:08:03
【问题描述】:

我需要保存允许执行某些操作的对象类型列表。

示例 Animal 有 3 个后代 TigerHumanHippo

我只想让Tigers 和Hippos 被关在动物园的笼子里?我需要一份动物类型列表。

我会喜欢比List&lt;Type&gt;更好的东西

这只是一个简化的例子。我不喜欢笼子里的动物..

编辑

因为不清楚。我想在列表中保存对象类型而不是实际对象。

例子:

List<Type> types = new List<Type>();
types.Add(typeof(Hippo));
types.Add(typeof(Tiger));

这是程序员可以做的限制types.Add(typeof(Human)),这是我不想反对的。

edit2

只是为了澄清我的问题。我希望能够动态地 Register 允许的类型,并且没有后续的 ifs 作为下面的一些答案。

【问题讨论】:

  • I want to allow only Tigers and Hippos to be held in zoo cages 这对我来说太寓言了。你能给出一个你想要做什么的代码示例吗?
  • 我猜你可以拥有 Animal 的 ZooAnimal 后代,并且只继承 Tiger 和 Hippo,然后拥有 List
  • List&lt;Type&gt; 有什么问题?
  • 在列表 中允许任何类型。我想限制它
  • @Veli 我不想要对象实例,而是列表中的对象 Types

标签: c# oop list inheritance


【解决方案1】:

如果您想要仅包含某些类型的列表:

泛型中没有任何东西可以支持您的要求,因此只需创建一个自定义类型,允许您存储 Type 类型并在运行时使用代码来防止无效条目:

public class CagedTypes 
{
    private readonly List<Type> _types;

    public void Add(Type t)
    {
        if (t == typeof(Hippo) || t == typeof(Tiger))
            _types.Add(t);
    }
}

虽然我不明白你为什么需要这个。

如果您想要仅包含某些类型的列表,则可以选择:

和上面一样,但是包括下面的接口,并将添加检查更改为:

public void Add(Type t)
{
    if (t.GetInterfaces().Contains(typeof(ICanBeHeldInZooCage)))
        _types.Add(t);
}

您也可以使用属性,因为您可以使用GetAttributes 方法查询任何属性的类型。

如果您只想在列表中包含某些实例:

创建标记界面:

public interface ICanBeHeldInZooCage

TigerHippo 实现(不需要做任何事情),那么你可以拥有:

var cagedAnimals = new List<ICanBeHeldInZooCage>();

【讨论】:

  • 正是我想要避免的多个if 部分。我已经编辑了这个问题,包括我想在运行时注册允许的类型
  • @odyodyodys 我已经用另一个选项修改了我的答案。它使用允许类型上存在接口的约定。
  • +1 表示所有其他选项,我个人更喜欢将类型存储在接口中,并使用该接口将类型保存在列表中。
【解决方案2】:

方法1 - 通过接口:

public interface ICageable
{ }

public abstract class Animal
{}

public class Hippo : Animal, ICageable
{}

public class Human : Animal, ICageable
{}

public IEnumerable<Type> GetCageableAnimals()
{
    return  GetAssemblyTypes(assembly:typeof(Animal).Assembly)
        .Where(type=>IsDerivedFrom(type, typeof(Animal)))
        .Where(type=>ImplementsInterface(type,typeof(ICageable)));
}

方法 2 - 通过属性:

public class InCageAttribute : Attribute
{ }

public abstract class Animal
{}

[InCage]
public class Hippo : Animal
{}

public class Human : Animal
{}

public IEnumerable<Type> GetCageableAnimals()
{
    return  GetAssemblyTypes(assembly:typeof(Animal).Assembly)
        .Where(type=>IsDerivedFrom(type, typeof(Animal)))
        .Where(type=>MarkedByAttribute(type,typeof(InCageAttribute)));
}

更新

重要

这两种方法都只提供运行时检查。有编译检查实现会更好,但不知道如何实现。

更新2
动态注册:

public class CageRegistry
{
    private List<Type> _allowedTypes = new List<Type>();
    public IEnumerable<Type> AllowedTypes{get{return _allowedTypes;}}

    public bool TryAdd(Type type)
    {
        if(ImplementsInterface(type, typeof(ICageable)))// for approach with attributes code is pretty similar
        {
            _allowedTypes.Add(type);
            return true;
        }
        return false;
    }
}

PS2
抱歉,没有实现像 MarkedByAttributeIsDerivedFromImplementsInterface 这样的方法 - 我只是在当前机器上还没有 Visual Studio,也不记得确切的 api。

【讨论】:

  • 我已经编辑了我的问题,包括我想动态注册允许的类型而不是编译类型。虽然我觉得你的答案很有趣
  • 是的,我已经阅读了您的更新,这就是我编写该代码的原因 - 认为它足够动态 :) 你可以动态调用它,无论它意味着什么 :) 说真的,你是什么意思通过“允许类型的动态注册”?
【解决方案3】:

接口怎么样?

public interface ICageable {}

public abstract class Animal {}

public class Hippo : Animal, ICageable {}

public class Tiger : Animal, ICageable {}

public class Human : Animal, ICageable {}

public class Ape : Animal {}

....
List<ICageable> ZooAnimals = new List<ICageable>{hippo, tiger, human};

(从人猿星球的角度写作)

如果您需要列表中的类型本身,那么类型是Type 类型的实例,因此无论您创建什么,它都是类型的集合。你可以这样封装:

public class CageableTypesCollection : 
{
    private List<Type> _cageableTypes;

    public CageableTypesCollection()
    {
       _cageableTypes = new List<Type>();
    }

    public RegisterType(Type t)
    {
       if (!typeof(ICageable).IsAssignableFrom(t))
          throw new ArgumentException("wrong type of type");
       _cageableTypes.Add(t);
    }

    public UnregisterType(Type t)
    {
       ....
    }

    .....

}

【讨论】:

  • 我想持有对象Types 而不是对象实例。这在问题标题和正文中都很清楚。我不明白为什么这个答案有赞成票。
  • @odyodyodys:添加了类型的东西,对不起,首先误读了问题,但将其作为序言。
  • 谢谢,这看起来很有希望。
【解决方案4】:

我会使用一个接口来确定一个动物是否是 ZooAnimal

  public class Animal
  {
    public string Name;
  }
  public class Tiger : Animal, IZooAnimal
  {

  }
  public class Human : Animal
  {

  }
  public interface IZooAnimal
  {
    //Some zoo animal properties
  }

然后检查该动物是否为 Zoo Animal if (a is IZooAnimal) 下面是您可以使用的 zoo 类。

  public class Zoo
  {
    public List<IZooAnimal> AnimalsInZoo = new List<IZooAnimal>();
    public void AddAnimal(IZooAnimal a)
    {
      AnimalsInZoo.Add(a);
    }
  }

编辑:

现在可以使用类型执行此操作并将类型限制为 ZooAnimal 我已经创建了一个通用动物园类,它采用 T 其中 T 是 ZooAnimal - 你可以有 ZooAnimals 列表或老虎列表我们的案例。

  public class Zoo<T> where T : IZooAnimal
  {
    public List<Type> AnimalTypes = new List<Type>();
    public void AddType(Type a)
    {
      if (typeof(T) == a)
        AnimalTypes.Add(a);
    }
  }

这会将type Tiger 添加到AnimalsInZoo。希望这对你有用。

  Zoo<IZooAnimal> cage = new Zoo<IZooAnimal>();
  cage.AddType(typeof(Tiger));
  cage.AddType(typeof(Human));

【讨论】:

  • @AgentFire 谢谢 - 太快了对我自己好,为什么我想通过一个动物并施放它而不是只在构造函数中接受 IZooAnimal 我永远不会知道!
  • @LukeHannerley 我想保存对象类型而不是对象实例
  • @odyodyodys 所以在List 中你想持有List&lt;Type&gt;,你永远不想持有实例?
  • public class Zoo&lt;T&gt; where T : IZooAnimal 更改为public class Zooif (typeof(T) == a) 更改为if (a.IsAssignableFrom(IZooAnimal))
【解决方案5】:

人是动物,老虎是应该在动物园里的动物。所以在你的情况下,我会为TigerHippo 再创建一个基类。

public class AnimalInZoo : Animal {}
public class Tiger : AnimalInZoo {}
public class Hippo : AnimalInZoo {}
public class Human : Animal {}

你可以创建辅助函数AddInZoo(AnimalInZoo obj)来添加你List&lt;Type&gt; m_Zoo

void AddInZoo(AnimalInZoo obj)
{
    m_Zoo.Add(obj.GetType());
}

【讨论】:

    【解决方案6】:

    另一个选项:

    public abstract class Animal
    {
        public abstract bool IsCagable { get; }
    }
    

    并让嵌套类实现它们的行为。

    然后,在本主题的答案中主要出现的某种Zoo 类,在方法 Add 中必须进行检查:

    public sealed class ZooList : List<Animal> // I believe you need Animal, not Type
    {
        // ... some implementations ...
    
        public override sealed void Add(Animal animal)
        {
            if (!animal.IsCagable)
                // Prevent from adding.
        }
    }
    

    【讨论】:

    • 由于 IsCageable 是一个实例变量,这可能意味着某种类型的某些实例(比如说人类)是可笼的,而有些则不是,所以它将从类型级别(OP需要它)到实例级别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 2010-12-27
    • 1970-01-01
    • 2021-06-26
    相关资源
    最近更新 更多