填充包裹<a> with Wrapped<b> classes where B implements A</b></a>的集合

时间:2013-05-28 23:04:54

标签: c# oop generics inheritance

所以我有以下简单的包装类:

interface IReference<out T> where T : myAbstractBase {
    T Value { get; }
}
public class Reference<T> : IReference<T> where T : myAbstractBase
{
    private T _value = null;
    public T Value {  get { return _value; } }
}

在我的整个应用程序中,我希望拥有这些IReference<someClass>个对象的集合(其中someClass实现myAbstractBase

private List<Reference<shapes>> shapeList = new Collection<Reference<shapes>>();

但我希望能够为这个系列添加各种不同的形状。 (特别是因为形状也是抽象的)。当然,这会产生错误:

shapeList.Add( new Reference<circle>(){ radius = 2; } );

The value "Reference[circle]" is not of type "Reference[shape]" and cannot be used in this generic collection.

我是否可以设计Reference<T>课程,只要A属于B类型,Reference<A>将被视为Reference<B>类型}?

在我看来,人们在尝试使用Nullable等列表时会遇到同样的问题。

我已经尝试实现隐式运算符来在Reference和T之间进行转换,但我没有想到它们的实际用途......

public class Reference<T> ... {
    ...

    public static implicit operator Reference<T>(T value)
    {
        return new Reference<T> { _value = value, };
    }
    public static implicit operator T(Reference<T> value)
    {
        return value.Value;
    }
}

对于任何对我的意图感到好奇的人来说,这都是(命运多)尝试为一组类实现延迟加载而不必向这些类添加更多内容的一部分。

4 个答案:

答案 0 :(得分:2)

您的问题是您无法链接用户定义的隐式强制转换。乍一看,您似乎应该可以从Reference<Circle> - &gt; Reference<Shape>来自Reference<Circle> - &gt; Circle - &gt; Shape - &gt; Reference<Shape>。但是,您将使用两个用户定义的隐式转换。首先,你要从Reference<Circle> - &gt; Circle来自operator T(Reference<T> value)。那你就去吧 Shape - &gt; Reference<Shape>来自operator Reference<T>(T value)。您可以通过扩展List来创建Add方法的重载来解决这个问题。这将使您可以在Reference.Add中明确使用其中一个用户定义的强制转换运算符。现在,您不必链接用户定义的隐式转换运算符。

请参阅用户定义的隐式转换的规范:http://msdn.microsoft.com/en-us/library/aa691302(v=vs.71).aspx

//You can get around your inability to chain user defined implicit casts
//by creating a ReferenceList<T> that extends List<IReference<T>>
//and overloads the List.Add method
public class ReferenceList<T> : List<IReference<T>> where T : MyAbstractBase
{
    //With this overload you can accept a T.  Then explicity cast to Reference<T>
    //by using operator Reference<T>(T value)
    public void Add(T item)
    {
        base.Add((Reference<T>)item);
    }
}
List<Reference<Shape>> shapeList = new List<Reference<Shape>>();
ReferenceList<Shape> shapeList2 = new ReferenceList<Shape>();
List<IReference<Shape>> shapeList3 = new List<IReference<Shape>>();

//Interesting cases that should work with the OP

//Works for obvious reasons
shapeList.Add(new Reference<Shape>());
//Works because you're using one user defined implicit cast 
//where the cast is operator Reference<T>(T value).
//Shape -> Reference<Shape>
shapeList.Add(new Shape());
//Works because you're using one non user defined implicit cast and one user defined 
//implicit cast where the user defined implicit cast is operator Reference<T>(T value)
//Circle -> Shape -> Wrapper<Shape>
shapeList.Add(new Circle());
//Does not work because you need to chain two user defined implicit casts
//where the implicit casts are operator T(Reference<T> value) and operator Reference<T>(T value)
//Reference<Circle> -> Circle -> Shape -> Reference<Shape>
//Theoretically this could work, but the C# specs state that chaining user defined
//implicit casts is not allowed in C# (See link below)
shapeList.Add(new Reference<Circle>());
//This case works for similiar reasons that shapeList.Add(new Circle()).  It uses
//only one user defined implicit cast because you're calling operator T(Reference<T> value)
//explicitely  
shapeList.Add(new (Circle)Reference<Circle>());

//Interesting cases for ReferenceList

//Works because this calls List.Add which accepts a Reference<T>
shapeList2.Add(new Reference<Shape>());
//Works because this calls ReferenceList.Add wich accepts a T
shapeList2.Add(new Circle());
//Works because this calls ReferenceList.Add wich accepts a T.
//and Reference<Circle> can be implicitly cast to a Circle via
//operator T(Reference<T> value).
//Reference<Circle> -> Circle -> Shape -> Reference<Shape> where
//the last cast is done explicitely in the ReferenceList.Add method
//via operator Reference<T>(T value)
shapeList2.Add(new Reference<Circle>());

//Interesting cases for List<IReference<Shape>>


//Works for obvious reasons
shapeList3.Add(new Reference<Shape>());
//Works because IReference is covariant.  In C# interfaces can be
//covariant.  Classes cannot be covariant.
shapeList3.Add(new Reference<Circle>());
//Does not work because C# does not support user defined implicit
//casts to interface.  In other words, you implicitly cast Shape -> Reference<Shape>
shapeList3.Add(new Shape());
//Doesn't work for similiar reasons to why shapeList3.Add(new Shape()) doesn't work
shapeList3.Add(new Circle());

答案 1 :(得分:0)

好的,这是一个黑暗的镜头,至少应该让你的隐式运算符编译。我没有任何设置,我可以直接测试它。它仍然应该工作。将其添加到Reference类。

public static implicit operator Reference<myAbstractBase>(Reference<T> i)
{
    return i;
}

请注意,此处缺少类型检查,因此如果T 不是派生自myAbstractBase,您可能会被软管化。

答案 2 :(得分:0)

我已经开始定义一个内部完全是List<Reference<T>>的自定义集合的路径,但它似乎正在做这个伎俩:

public class ReferenceCollection<T> : ICollection<T> where T : myAbstractBase 
{
    private List<Reference<T>> collection = new List<Reference<T>>();

    public IEnumerable<T> toIEnumerable()
    {
        return (IEnumerable<T>) collection.Select(r => r.Value);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return toIEnumerable().GetEnumerator(); ;
    }

    #region ICollection<T> Members
    public void Add(T item)
    {
        collection.Add(item);
    }
    ...
}

现在我可以拨打电话(之前我做不到):

ReferenceCollection<shape> test = new ReferenceCollection<shape>();
test.Add(new circle());

为什么这样工作而不是另一种?我真的在做任何不同的事吗?也许转换实际上现在正以相反的顺序发生(圆圈被塑造成形状,然后随着它被添加到内部集合中,形状会被隐式转换为Resource<shape>。)

我看不出这种方法有什么不足之处。我甚至可以定义隐式转换器,将ReferenceCollection<T>直接转换为List<T>List<Reference<T>>,而无需迭代它。不过,我想知道是否有一种方法来定义原始类,以便在隐式转换之前类似地发生转换以避免不可转换的包装类型。

答案 3 :(得分:0)

我改写了这个问题并在另一个帖子中得到了答案:How to cast a Generic<T> to a Generic<R> where T is a subclass of R?

诀窍是创建协变接口IReference<out T>并始终在使用类型的地方使用接口。因此声明List<IReference<myAbstractBase>>而不是List<Reference<myAbstractBase>>。详细说明为何在Covariance and Contravariance in Generics

工作