填充一个Wrapped与Wrapped< B>类,其中B实现A
本文关键字:Wrapped 其中 实现 填充 一个 | 更新日期: 2023-09-27 18:15:42
所以我有以下简单的包装器类:
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;
}
}
对于任何对我的意图感到好奇的人来说,这都是(不幸的)尝试的一部分,目的是实现一组类的延迟加载,而不必不必在这些类中添加任何东西。
您的问题是您不能链接用户定义的隐式强制转换。乍一看,似乎你应该能够从Reference<Circle>
-> Reference<Shape>
通过Reference<Circle>
-> Circle
-> Shape
-> Reference<Shape>
。但是,您将使用两个用户定义的隐式强制转换。首先,您将从Reference<Circle>
-> Circle
通过operator T(Reference<T> value)
。然后你会从Shape
-> 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());
好了,这里有一个提示,至少可以使您的隐式操作符编译。我没有任何可以马上测试的设置。不过,这应该是可行的。将其添加到Reference类中。
public static implicit operator Reference<myAbstractBase>(Reference<T> i)
{
return i;
}
注意,这里缺少类型检查,所以如果T 不是从myAbstractBase派生的,那么您可能会遇到麻烦。
我已经开始定义一个自定义集合的路径,这正是一个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());
为什么它是这样工作的,而不是另一个?我真的做了什么与众不同的事吗?也许转换现在实际上是以相反的顺序发生的(circle被转换为shape,然后shape被隐式转换为Resource<shape>
,因为它被添加到内部集合中。)
我还看不出这种方法有什么缺点。我甚至可以定义隐式转换器,将ReferenceCollection<T>
直接转换为List<T>
或List<Reference<T>>
,而无需对其进行迭代。不过,我想知道是否有一种方法可以定义原始类,使强制转换类似地发生在隐式转换之前,以避免不可强制转换的包装类型。
我重新提出了这个问题,并在另一个线程中得到了答案:如何转换泛型
诀窍是创建一个协变接口IReference<out T>
,并且总是在使用类型的地方使用该接口。所以声明List<IReference<myAbstractBase>>
而不是List<Reference<myAbstractBase>>
。关于为什么这在泛型中的协方差和逆变中起作用的细节