受限类型的通用集合

3
我有一个需要以下定义的班级:
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的原因。
有人知道我该如何解决这个问题吗?

1
为什么不使用 Dictionary<Type, object> 并在需要时进行强制转换? - CodeCaster
3个回答

1
问题在于需要一个数据表的集合,但是 Table<X>Table<Y> 不兼容,而且 X 是一个接口类型,Y 实现了这个接口,但是 WhateverCollection<Table<X>>WhateverCollection<Table<Y>> 也不兼容。

为什么会这样呢?假设你有

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

Put that in your pipe and smoke it!

// 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<T>实现ITable,然后将字典声明为Dictionary<Type, ITable>


我声明了一个空的ITable接口,我的Table<T>类声明了它的实现。我的TableCollection类实现了IEnumerable<ITable>,而我的_tableDictionary被声明为Dictionary<Type, ITable>,它运行得很好。谢谢。 - sbeaudoin
您还可以将所有不依赖于泛型类型 T 的常见表成员添加到 ITable 中。例如,所有表都可以具有 int ID 列和 Save() 方法。这使接口变得更加有用。顺便说一下:如果一个接口没有任何成员,则被称为“标记接口”。 - Olivier Jacot-Descombes

0
你可以移除new()限制,使用Activator.CreateInstance<T>()来创建新对象。这将把检查从编译时转移到运行时。C#编译器无论如何都会将new T()翻译成一个Activator.CreateInstance调用。

如果 OP 不能修改 Table 类,将会怎样? - pwas
@nopeflow他说了什么?我没听到。那么他需要使用具体的类类型,或者他需要编写类似于Dictionary<Type, Table<T>>这样的代码,其中T被适当地限制为IRowDef, new()。表达一个类型具有new()的唯一方法是使用泛型。 - usr
1
不,他没有这样做,但我很好奇在那种情况下可以做些什么。此外,使用Dictionary<Type, Table<T>>并不是解决方案,在我看来,因为OP想要将不同类型的映射存储为字典值。 - pwas
@nopeflow 这是一个很好的观点。那么他就没办法了,因为他不能拥有所有东西。他必须放松一些限制。在字典中限制类型、放弃 new(),或者存储像 object 或另一个基类这样的东西,并在运行时进行转换。我有点不喜欢这些复杂的泛型问题。如果你必须问,那么设计已经太复杂了。我认为他应该放弃 new 的限制。 - usr

0

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;

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