【发布时间】:2017-04-28 21:51:04
【问题描述】:
假设我有以下类用于包装给定类型的一些项目:
public class IndexedModelWithValue<T>
{
public IndexedModelWithValue(T value, int index)
{
Index = index;
InnerValue = value;
}
public int index;
public T InnerValue { get; }
}
我有这两个类:
class Animal { }
class Dog : Animal { }
在某些时候,我加载了不同的对象(这里我们将只使用 Dog 类),它们都继承自同一个基类型,所以我会有这样的东西:
Dog dog = LoadMyDerivedAnimalFromSomewhere();
Animal animal = dog;
// In my actual code I have something like
IEnumerable<Animal> animals = LoadMyDerivedAnimalsFromSomewhere();
然后继续包装所有这些项目,以便我最终得到如下内容:
IndexedModelWithValue<Animal> indexedAnimal = new IndexedModelWithValue(animal, 0);
现在,我知道索引模型中的动物实际上是一个 Dog 实例,那么有没有办法从那个实例中得到一个 IndexedModelWithValue<Dog> 对象?
我考虑过使用接口(我不介意以IIndexedModelWithValue<Dog> 之类的东西结束,只要我在那里得到Dog 类型),但据我了解,协变类只能强制转换在相反的方向(IEnumerable<String> 到 IEnumerable<object,反之亦然),我不能在这里使用逆变类,因为该类公开了一个 T 参数。
感谢您的帮助!
编辑:为澄清起见,这是我最终的清单:
IEnumerable<Animal> animals = LoadAllMyAnimalsFromSomewhere();
IEnumerable<IndexedModelWithValue<Animal>> = animals.Select((item, i) => new IndexedModelWithValue(item, i));
临时(坏)解决方法:现在我使用的解决方案是向索引类添加一个方法:
public IndexedModelWithValue<TValue> Downcast<TValue>() where TValue : T
{
return new IndexedModelWithValue<TValue>((TValue)InnerValue, Index);
}
这样我就可以做到:
IndexedModelWithValue<Dog> indexedDog = indexedAnimal.Downcast<Dog>();
唯一的问题是这样我每次调用这个方法时都会创建IndexedModelWithValue类的一个新实例(所以我在浪费内存和CPU时间)而事实上我只想将该对象转换为我的派生类型。
解决方案:这是我现在用来解决问题的方法:
public static IEnumerable<ICovariantIndexedModelWithValue<T>> New([NotNull] IEnumerable<T> source)
{
int index = 0;
foreach (T item in source)
{
object instance = Activator.CreateInstance(typeof(IndexedModelWithValue<>).MakeGenericType(item.GetType()), item, index);
yield return (ICovariantIndexedModelWithValue<T>)instance;
index++;
}
}
我可以调用它并传递原始列表以获得协变包装列表,其中每个单独的项目都具有“正确的类型”,具体取决于其派生类型。
界面如下:
public interface ICovariantIndexedModelWithValue<out T>
{
int Index { get; }
T InnerValue { get; }
}
【问题讨论】:
标签: c# generics covariance