我们说我有以下用于包装给定类型的项目的类:
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参数。 / p>
感谢您的帮助!
修改:有关说明,请参阅我的列表:
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++;
}
}
我可以调用它并传递原始列表以获得协变包装列表,其中每个单独的项目具有&#34;右类型&#34;取决于其派生类型。
界面如下所示:
public interface ICovariantIndexedModelWithValue<out T>
{
int Index { get; }
T InnerValue { get; }
}
答案 0 :(得分:1)
您的indexedDog
和indexedAnimal
是两种不同的类型。它不仅仅是将它们存放在不同形状的盒子中,实际类型也不同。因此,如果不创建新对象,就无法做到这一点。如果你想避免浪费内存,cpu或其他任何东西,那么你首先需要将对象创建为IndexedModelWithValue<Dog>
。如果你知道它绝对是一只狗,那么这就是你应该做的。如果你有一只明确的狗,那么首先不要创建一个IndexedModelWithValue<Animal>
,你的问题就会消失。如果没有看到创建对象的代码,很难确切知道如何执行此操作,但泛型应该允许您创建所需的类型。
答案 1 :(得分:0)
如果对象本身是强类型创建的(例如,当为IndexedModelWithValue
创建Dog
时,它将被设为IndexedModelWithValue<Dog>
而不是IndexedModelWithValue<Animal>
) ,接口可用于支持(co)方差和存储在接口类型列表中的所有不同值。
编辑基于类型Animal
的源:尽管构建时间检查不是一个非常好的解决方案,但可以创建强类型IndexedModelWithValue
Activator.CreateInstance
和typeof(IndexedModelWithValue<>).MakeGenericType(a.GetType())
(其中a是Animal
变量)。
(如果加载数据的方法可能以某种方式创建其他对象,则可以添加更好的设计时功能)
根据您当前的示例,可以使用以下命令创建枚举:
LoadAllMyAnimalsFromSomewhere().Select((a,i)=> (IIndexedModelWithValue<Animal>) Activator.CreateInstance(typeof(IndexedModelWithValue<>).MakeGenericType(a.GetType()), a,i));
Linqpad安全代码:
void Main()
{
var list = LoadData().Select((a,i)=> (IIndexedModelWithValue<Animal>) Activator.CreateInstance(typeof(IndexedModelWithValue<>).MakeGenericType(a.GetType()), a,i)).ToList();
foreach(var t in list.OfType<IIndexedModelWithValue<Dog>>())
Console.WriteLine(t);
}
static IEnumerable<Animal> LoadData()
{
yield return new Dog();
yield return new Gecko();
}
public class IndexedModelWithValue<T>:IIndexedModelWithValue<T>
{
public IndexedModelWithValue(T value, int index)
{
Index = index;
InnerValue = value;
}
public int Index{get;set;}
public T InnerValue { get; }
}
public interface IIndexedModelWithValue<out T>
{
int Index{get;}
T InnerValue {get;}
}
class Animal { }
class Dog : Animal { }
class Gecko:Animal{}