Вы можете ввести неуниверсальный интерфейс IFoo:
public interface IFoo
{
RecordType Type { get; set; }
}
, который реализуется универсальным классом Foo:
public class Foo<T> : IFoo
{
public T Value { get; set; }
}
и создайте фабричный метод, который создает экземпляр Foo в зависимости от RecordType:
public static IFoo CreateFoo(RecordType type)
{
switch (type)
{
case RecordType.Bool: return new Foo<bool>();
// ...
}
}
После того, как вы создали экземпляр Foo таким способом, вы еще не можете получить доступ к значению, потому что вы еще не знаете параметр типа. Но вы можете проверить тип с помощью свойства Type и выполнить соответствующее приведение:
IFoo foo = CreateFoo(RecordType.Bool);
if (foo.Type == RecordType.Bool)
{
Foo<bool> boolFoo = (Foo<bool>)foo;
bool value = boolFoo.Value;
}
Если у вас есть метод, который работает с объектами Foo, например:
void DoIt<T>(IEnumerable<Foo<T>> foos)
{
foreach (Foo<T> foo in foos)
{
Qux(foo.Value);
}
}
и имея множество объектов IFoo, вы можете использовать Cast / OfType, которые:
IEnumerable<IFoo> foos = // ...
DoIt<bool>(foos.OfType<Foo<bool>>());
Итак, по сути, вы используете Foo , когда вы знаете T во время компиляции, и IFoo, если вы знаете T только во время выполнения. IFoo требует проверки, чтобы превратить его в Foo для некоторого T во время выполнения.