【问题标题】:Storing a list of different generic types in a class在类中存储不同泛型类型的列表
【发布时间】:2016-04-07 06:04:31
【问题描述】:

我附上了一张我正在尝试做的事情的图片。假设我有一个类中的 T 列表

public class MyClass<T> 
    where T : IMyInterface
{
    public List<T> list = new List<T>;
}

现在,另一个类有一个 MyClass 列表。

public class AnotherClass
{
    public List<MyClass<IMyInterface>> list = new List<MyClass<IMyInterface>>;
}

我应该为 MyClass 放置什么 T?如果我输入 T,那么它假定该类中的所有类型都是相同的,但事实并非如此。如果我放 IMyInterface,在访问这些类时我无法将 IMyInterface 强制转换为 T。

new AnotherClass().list.Add(new MyClass<T>()); 

现在这里的类型是什么? AnotherClass 泛型类型中的列表是 IMyInterface,但我要添加的是 T,我希望它是动态的,但它们仍在 IMyInterface 下。

【问题讨论】:

  • 问题在于您描述的是问题的解决方案,而不是实际问题。你想解决什么??? (XY Problem).
  • 我已经有一段时间没有使用 c# 了,但我不会使用泛型。如果您知道您只将实现您的接口的对象放入其中,那么为什么不使用多态。这意味着: public List list=new List;
  • 你似乎把事情搞糊涂了。泛型不是动态类型的。 C# 中的所有内容都是静态类型的,除了涉及 dynamic 关键字的语句/声明,但这与泛型无关。 List&lt;int&gt; 是一个仅包含 ints 的列表。 a List&lt;IMyInterface&gt; 是一个列表,仅包含实现 IMyInterface 的对象。

标签: c# list class generics dynamic


【解决方案1】:

泛型中的T 在其整个生命周期内必须相同。

举个例子

var a = new MyClass<int>();
a.list.Add(1); //since a's generic T is declared as int the list is now also int

var b = new MyClass<string>()
b.list.Add("string"); //in b its now declared as a string and avccepts only string

所以当你这样做的时候。

var c = new MyClass<IMyInterface>();
c.list.Add(ObjectImplementingIMyInterface); //You must add something that implements IMyInterface

关于内容你唯一知道的是它现在实现了IMyInterface,如果你想拆分对象的执行,那么你必须通过反射检查对象的类型。

if(c.list[i].GetType() == typeof(ClassA_IMyInterface)
{
    //Execute on ClassA_IMyInterface
    //If you are after the ClassA 
    //and want to run something speciffic for it the cast
    ClassA clA = = (ClassA)c.list[i];
    //Now you have access to the implementation of ClassA instead of just the interface 
}
else if (c.list[i].GetType() == typeof(ClassB_IMyInterface)
{
    //Execute on ClassB_IMyInterface
}

这是我在 ConsoleApplication 中制作的一个示例,展示了它的布局方式。

public class MyClass<T> where T : IMyInterface
{
    public List<T> list = new List<T>();
}
public interface IMyInterface
{
}
public class Foo : IMyInterface
{
}
public class Bar : IMyInterface
{
}
public class FooBar
{
    public void Test()
    {
        var content = new MyClass<IMyInterface>();
        content.list.Add(new Foo());
        if (content.list[0] is Foo)
        {
            //Execute on Foo 
            var g = (Foo)content.list[0];
            //Now you can access Foo's methods and not only the Interfaces
            Console.WriteLine("Foo");
        }
        else if (content.list[0] is Bar)
        {
            //Execute on Bar
            var g = (Bar)content.list[0];
            //Now you can access Bar's methods and not only the Interfaces
            Console.WriteLine("Bar");
        }
    }
}

【讨论】:

  • c.list[0] is dd1。这允许使用 dd1 的代码处理 dd1 的子类。
  • @Taemyr is 是一个布尔评估器(我可能会用它来代替GetType/typeof。你在想as(接近铸造,但允许结果为@ 987654331@)
  • 我的意思是代替 GetType()。在您的示例中使用 as 没有意义。
  • 是的,像这样使用is 会更好一些,因为它是一种语法糖。
【解决方案2】:

我认为您的问题是您不需要为此使用泛型。如果您将班级指定为:

public class MyClass
{
    public IList<IMyInterface> list = new List<IMyImterface>();
}

然后你可以添加任何实现接口的类的任何实例。

public class DummyClass : IMyInterface {}

public class AnotherClass {
{
    IList<MyClass> anotherList = new List<MyClass>();

    public void AMethod()
    {
        MyClass myList = new MyClass();
        myList.Add(new DummyClass());

        anotherList.Add(myList);
    }
}

然后您可以直接以 IMyInterface 的形式访问列表中的所有项目,或者如果您想要特定类的项目,您可以使用 LINQ:

foreach (IMyImterface item in myList.Where(x => x is DummyClass))
{
    DummyClass dummy = (DummyClass)item;
}

// or
foreach (IMyImterface item in myList.OfType<DummyClass>())
{
    DummyClass dummy = (DummyClass)item;
}

或者您可以尝试强制转换并检查是否为空:

foreach (IMyIntetface item in myList)
{
    DummyClass dummy = item as DummyClass;
    if (dummy != null)
    {
        // do something
    }
}

【讨论】:

  • 从序列中获取特定子类型的所有实例的规范方法是seq.OfType&lt;T&gt;()(您的版本进行两次类型检查)。尽管无论如何你都不应该在 100 个案例中的 99 个中贬低东西。
  • 感谢您的提示。我希望这会更快?
  • 好吧,我假设时间复杂度是相同的,但我并不担心性能,两者都足够快,永远不会成为您应用程序的瓶颈。说你想要什么更干净,而不是说应该做什么,知道它会产生你想要的。这是一种声明性与命令性的事情。
  • @SteveHarris 我认为演员(DummyClass)item 会抛出异常而不是返回null。应该是item as DummyClass
【解决方案3】:

我认为您的意思是您希望在一个列表中包含多个泛型类型,例如:

var ac = new AnotherClass();
ac.list.Add(new MyClass<int>()); 
ac.list.Add(new MyClass<bool>()); 
ac.list.Add(new MyClass<double>());

一种简单的方法是将您的列表定义为对象列表:

public class AnotherClass
{
    public List<object> list = new List<object>();
}

这里的问题是您可以在该列表中添加任何您喜欢的内容,而不仅仅是您的通用类。以下也是可能的:

list.Add(1); 
list.Add("hello"); 

您可以限制添加内容的唯一方法是使用抽象基类。

public class MyClassBase 
{

}
public class MyClass<T> : MyClassBase
    where T : IMyInterface
{
    public List<T> list = new List<T>();
}

然后列出这些:

public class AnotherClass
{
    public List<MyClassBase> list = new List<MyClassBase>();
}

在访问这些对象时,您仍然需要进行一些类型检查,但至少您会知道这些对象仅限于那些继承 MyClassBase 的对象

【讨论】:

    【解决方案4】:

    使用covariant?

    public interface IMyInterface { }
    public class A : IMyInterface { }
    public class B : IMyInterface { }
    
    // Covariant interface
    public interface IMyClass<out T> { }
    
    // Inherit IMyClass
    public class MyClass<T> : IMyClass<T> where T : IMyInterface 
    { 
        public List<T> list = new List<T>(); 
    }
    
    public class AnotherClass
    {
        // Note the list using IMyClass instead of the concrete MyClass type
        // The covariant interface type would allow any IMyInterface conversion
        public List<IMyClass<IMyInterface>> list = new List<IMyClass<IMyInterface>>();
    
        public AnotherClass()
        {
            this.list.Add(new MyClass<A>());
            this.list.Add(new MyClass<B>());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-29
      • 1970-01-01
      • 2012-08-05
      • 2020-05-30
      • 1970-01-01
      • 2013-06-01
      • 2017-05-06
      • 1970-01-01
      相关资源
      最近更新 更多