【问题标题】:Generic collection of restricted types受限类型的通用集合
【发布时间】:2016-01-09 00:06:44
【问题描述】:

我有一个需要以下定义的类:

public class Table<T> : ObservableCollection<T> where T : IRowDef, new()

我想创建它的集合并使用实例映射类型。所以我尝试:

public sealed class TableCollection : IEnumerable<Table<IRowDef>>
{
   private Dictionary<Type, Table<IRowDef>> _tableDictionary;

   public Table<IRowDef> GetTable<T>() where T : IRowDef, new()
   {
        Table<IRowDef> table = null;

        if (_tableDictionary.ContainsKey(typeof(T)))
        {
            table = _tableDictionary[typeof(T)];
        }
        else
        {
            table = new Table<IRowDef>();
            _tableDictionary.Add(typeof(T), table);
        }

        return table;
   }

   ...
}

但我不能让它工作。以下几行和其他几行给出了相同的错误:

private Dictionary<Type, Table<IRowDef>> _tableDictionary;

翻译后的错误告诉 IRowDef 必须是非抽象的并且具有无参数的构造函数。我知道它来自 Table 类定义的“new()”类型限制,但它是此类内部代码所必需的。我知道我可以通过使用包含无参数构造函数的特定类类型来解决这个问题,例如:

private Dictionary<Type, Table<ClientTable>> _tableDictionary;

但必须支持不同类型的表,这也是它们都实现 IRowDef 的原因。

有人知道我该如何解决这个问题吗?

【问题讨论】:

  • 为什么不使用Dictionary&lt;Type, object&gt; 并在需要时进行转换?

标签: c# generics collections covariance


【解决方案1】:

问题是您需要一个表的集合,但Table&lt;X&gt;Table&lt;Y&gt; 不兼容,WhateverCollection&lt;Table&lt;X&gt;&gt;WhateverCollection&lt;Table&lt;Y&gt;&gt; 不兼容,即使X 是接口类型而Y 实现了这个接口。

为什么会这样?假设你有

List<IAnimal> animals = new List<Elefant>();
animals.Add(giraffe); // Ooops!

把它放进你的烟斗里抽!

// DOES NOT WORK!  
T<Base> b = new T<Derived>(); // T<Derived> is not assignment compatible to T<Base>!!!

但是

Base b = new Derived(); // OK

诀窍是有两个表类:一个非泛型基类和一个派生泛型类:

public abstract class Table
{}

public class Table<T> : Table
     where T : IRowDef, new()
{
     private readonly ObservableCollection<T> _rows = new ...;
}

现在你可以声明一个

private Dictionary<Type, Table> _tableDictionary;

或者,如果您想坚持从可观察集合派生,请声明(非泛型!)ITable 接口而不是 Table 基类,并让 Table&lt;T&gt; 实现 ITable 然后声明字典作为Dictionary&lt;Type, ITable&gt;

【讨论】:

  • 我声明了一个空的 ITable 接口,我的 Table 类声明了它的实现。我的 TableCollection 类实现了 IEnumerable 并且我的 _tableDictionary 被声明为 Dictionary 并且效果很好。谢谢。
  • 您还可以将不依赖于泛型T的所有表的常用成员添加到ITable。例如,所有表都可以有一个int ID 列和一个Save() 方法。这使得界面更有用。顺便说一句:没有任何成员的接口称为标记接口
【解决方案2】:

您可以删除new() 约束并使用Activator.CreateInstance&lt;T&gt;() 创建新对象。这会将检查从编译时移到运行时。 C# 编译器将 new T() 转换为 Activator.CreateInstance 调用。

【讨论】:

  • 如果 OP 无法修改 Table 类怎么办?
  • @nopeflow 他是不是这么说的?我错过了。然后他需要使用具体的类类型,或者他需要编写类似Dictionary&lt;Type, Table&lt;T&gt;&gt; 的东西,其中T 被适当地限制为IRowDef, new()。说一个类型有new() 的唯一方法是使用泛型。
  • 不,他没有,但我很好奇在那种情况下可以做些什么。更重要的是,使用Dictionary&lt;Type, Table&lt;T&gt;&gt; 不是解决方案恕我直言,因为 OP 希望将不同类型的映射存储为字典值。
  • @nopeflow 说得好。然后他就不走运了,因为他不能拥有这一切。他必须放松一些约束。限制字典中的类型,删除 new(),或存储 object 或其他基类之类的内容并在运行时强制转换。我有点不喜欢这些复杂的泛型问题。如果你要问设计已经很复杂了。我认为他应该放弃新的约束。
【解决方案3】:

Olivier Jacof-Descombes 提出了一种可能的方法。另一种(仅适用于可以修改Table类):

public interface ITable
{
    //Some needed methods, f,e,
    IRowDef GetSth();
}

然后:

public class Table<T> : ..., ITable where T : IRowDef, new()
{
    IRowDef ITable.GetSth()
    { 
         return (IRowDef)this.GetSthImplInsideTable(); // cast is optional
    }

    public T GetSthImplInsideTable() { /* impl */ }
}

您可以将其用作:

private Dictionary<T, ITable> _tablesDict;

【讨论】:

    猜你喜欢
    • 2016-11-03
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 2011-04-02
    • 2021-05-27
    相关资源
    最近更新 更多