15

好的,情况就是这样。我有一个FlexCollection<T>类,其目的是保存一些专业化的列表FlexItem,因此:

public class FlexCollection<T> where T : FlexItem, new()
{
    public void Add(T item) { ... }
    ...
}

FlexItem不是泛型类本身。我想要实现的是能够在FlexItem's 字段中保存对包含该对象的集合的引用。不幸的是,在 C# 中,不可能引用模板类的“任何”特化(如在 Java 中)。起初我尝试使用非泛型接口IFlexCollection,但它实际上迫使我实现每个方法两次,即:

public class FlexCollection<T> : IFlexCollection where T : FlexItem, new()
{
    public void Add(T item) { ... } // to use in generic calls
    public void Add(object item) { ... } // to use for calls from within FlexItem
    ...
}

然后我发现我可以让 FlexItem 本身成为一个泛型类!然后一个特化可以持有对该特化的对象集合的引用(这是非常合乎逻辑的)。所以:

public class FlexItem<T> where T : FlexItem<T>, new()
{
    public FlexCollection<T> ReferenceToParentCollection;
    ...
}

public class FlexCollection<T> where T : FlexItem<T>, new()
{
    public void Add(T item) { ... } 
    ...
}

现在我可以声明一些FlexItem<T>专业化和相应的集合:

public class BasicItem : FlexItem<BasicItem> { public int A; }
public class BasicCollection : FlexCollection<BasicItem> { };

当我尝试扩展这些类以容纳其他字段时,就会出现问题。即我想要一个除了 field 之外还ExtendedItem包含 field 的类:BA

public class ExtendedItem : BasicItem { public int B; }
public class ExtendedCollection : FlexCollection<ExtendedItem> { };

问题是它ExtendedItemFlexItem<BasicItem>and not的子类FlexItem<ExtendedItem>。因此是不可能ExtendedCollection像上面那样声明的。这会导致编译错误:

The type 'Demo.ExtendedItem' must be convertible to
'Demo.FlexItem<Demo.ExtendedItem>' in order to use it as parameter 'T'
in the generic type or method 'Demo.BasicCollection<T>'

有没有办法避免这种类型的冲突?

4

3 回答 3

1

有两种方法可以解决此问题:

您可以使用基本多态集合而不继承基类集合:

class Program
{
    static void Main(string[] args)
    {
        BasicCollection extendedCollection = new BasicCollection();
        extendedCollection.Add(new ExtendedItem { A = 1, B = 2});
        extendedCollection.Add(new BasicItem { A = 3 });
        extendedCollection.Add(new ExtendedItem { A = 4, B = 5});

        foreach (BasicItem item in extendedCollection)
        {
            switch(item.GetType().Name)
            {
                case "BasicItem":
                    Console.Out.WriteLine(string.Format("Found BasicItem: A={0}", item.A));
                    break;
                case "ExtendedItem":
                    ExtendedItem extendedItem = item as ExtendedItem;
                    Console.Out.WriteLine(string.Format("Found ExtendedItem: A={0} B={1}", extendedItem.A, extendedItem.B));
                    break;
            }
        }
    }
}

public class FlexItem<T> where T : FlexItem<T>, new()
{
    public FlexCollection<BasicItem> ReferenceToParentCollection;
}

public class FlexCollection<T> where T : FlexItem<T>, new()
{
    public void Add(T item) { } 
}
public class BasicItem : FlexItem<BasicItem> { public int A; }
public class ExtendedItem : BasicItem { public int B; }
public class BasicCollection : FlexCollection<BasicItem>
{
    Collection<BasicItem> items = new Collection<BasicItem>();
    public void Add(BasicItem item)
    {
        item.ReferenceToParentCollection = this;
        items.Add(item);
    }
    public void Remove(BasicItem item)
    {
        item.ReferenceToParentCollection = null;
        items.Remove(item);
    }
    public IEnumerator GetEnumerator()
    {
        return items.GetEnumerator();
    }

}

或者您可以将集合类引用装箱并在需要时将其拆箱,因为您知道子对象的类型?

class Program
{
    static void Main(string[] args)
    {
        ExtendedCollection extendedCollection = new ExtendedCollection();
        extendedCollection.Add(new ExtendedItem { A = 1, B = 2, ReferenceToParentCollection = extendedCollection });
        extendedCollection.Add(new ExtendedItem { A = 3, B = 3, ReferenceToParentCollection = extendedCollection });
        foreach (ExtendedItem item in extendedCollection)
        {
            (item.ReferenceToParentCollection as ExtendedCollection) ...
        }
    }
}


public class FlexItem<T> where T : FlexItem<T>, new()
{
    public object ReferenceToParentCollection;
}

public class FlexCollection<T> where T : FlexItem<T>, new()
{
    public void Add(T item) { } 
}

public class BasicItem : FlexItem<BasicItem> { public int A; }
public class BasicCollection : FlexCollection<BasicItem> { };
public class ExtendedItem : BasicItem { public int B; }
public class ExtendedCollection : FlexCollection<ExtendedItem> { };
于 2012-06-29T18:35:43.157 回答
1

好的,我决定尝试 C# 事件。这样,我实际上不需要 FlexItem 来保存对拥有 FlexCollection 的引用。如果执行相应的操作,我只需引发一个事件,即“IAmBeingRemoved”和 FlexCollection 的代码。代码逻辑甚至更好地封装。我只是希望我不会遇到任何性能问题或其他基于泛型的惊喜。:-) 我发布它是为了也许有人会发现它是解决他们自己问题的好方法。

于 2012-06-28T12:45:48.057 回答
0

不确定您的代码的目的(用途)是什么,但在我看来,您正在尝试基于泛型解决方案以及稍后“摆脱”有关某些类型的泛型数据 - 这在 C# 中是不可能的. 如果您选择采用泛型方法,则应始终保留泛型类型基础的信息:

    public class FlexItem<T> where T : FlexItem<T>, new()
    {
        public FlexCollection<T> ReferenceToParentCollection;
    }

    public class FlexCollection<T> where T : FlexItem<T>, new()
    {
        public void Add(T item)
        {
        }
    }

    public class BasicItem<T> : FlexItem<BasicItem<T>>
    {
        public int A;
    }

    public class BasicCollection<T> : FlexCollection<BasicItem<T>>
    {
    }

    public class ExtendedItem<T> : BasicItem<T>
    {
        public int B;
    }

    public class ExtendedCollection<T> : FlexCollection<T> where T: FlexItem<T>, new()
    {
    }
于 2015-02-05T16:24:51.147 回答