我有一个通用的方法
public async Task Save<T>(T aggregate) where T : AggregateRoot
从这个方法我调用另一个泛型方法
var indexRegistrations = IndexRegistrar.GetAll<T>();
现在在第二种通用方法中,我希望获得T
的实际类型,它是AggregateRoot
的子类型:
public static List<IndexRegistration> GetAll<T>() where T : AggregateRoot
{
return _register.FindAll(r => r.aggregateType == typeof(T));
}
但是,typeof(T)
始终返回AggregateRoot
。
如何获取AggregateRoot
的真实类型(= T
的子类型)?
答案 0 :(得分:5)
typeof(T)
在这里是正确的。我测试了你的案例,typeof(T)
总是返回&#34; true&#34;类,而不仅仅是类型要求。
public class BaseClass { }
public class DerivedClass: BaseClass { }
public class GenericClass<T> where T : BaseClass
{
public string TypeOf = typeof(T).ToString();
}
public class GenericSuperClass<T> where T : BaseClass
{
public GenericClass<T> Sub = new GenericClass<T>();
}
static void Main(string[] args)
{
Console.WriteLine("1 - " + (new GenericClass<BaseClass>()).TypeOf);
Console.WriteLine("2 - " + (new GenericClass<DerivedClass>()).TypeOf);
Console.WriteLine("3 - " + (new GenericSuperClass<BaseClass>()).Sub.TypeOf);
Console.WriteLine("4 - " + (new GenericSuperClass<DerivedClass>()).Sub.TypeOf);
Console.ReadLine();
}
输出:
1 - BaseClass
2 - DerivedClass
3 - BaseClass
4 - DerivedClass
请注意,我已经从实际返回的值中简化了类名(例如Sandbox.TestConsole.Program+DerivedClass
)。
这与您声称只获得基本类型(AggregateRoot
,在您的情况下)的说法直接相矛盾。
我可以想到一个例外:当您的类型仅在运行时中定义时(例如,从类型名称(String
)生成)。
但是,正如this StackOverflow answer所解释的那样,泛型旨在提供编译时类型的安全性。
使用反射在运行时实例化泛型类并非不可能。但是当你这样做时,你固有地阻止了在编译时决定的信息(例如类型名称)的有效性。
MSDN's page on typeof隐含地声明typeof
的返回值是编译时类型。
结合这两个事实,这意味着当你使用反射(即决定运行时的类型)时,你不能依赖typeof
(因为这会返回编译时间) 类型)。
链接的MSDN页面还提到了如何找到运行时类型:
要获取表达式的运行时类型,可以使用.NET Framework方法GetType,如以下示例所示:
int i = 0; System.Type type = i.GetType();
但请注意,GetType()
方法仅适用于实例化对象,而不适用于泛型类型参数。通用类型参数不是真正的类型,它们更接近&#34;类型占位符&#34;。
您需要将类型作为参数传递,或者实例化对象(适当类型)。
如果您使用的是编译时已知的类型,那么您可以简单地使用typeof(T)
,如我的示例所示。
如果使用反射决定运行时的类型,则不能依赖typeof(T)
提供的信息,因此需要提供该类型。您可以将其作为Type
参数提供,或者通过实例化对象提供它,运行时类型可以使用GetType()
方法进行准确测试。
但是,如果您已经在运行时决定类型,那么最好将类型本身作为参数传递。如果你在这里使用反射,那意味着你必须在某个时候知道你想要使用哪种类型。因此,只需将该已知类型作为参数传递,然后可以将其用于后续业务逻辑。
我想不出一个单一的情况,你不是(a)使用在编译时已知的类型,也不是(b)知道(在运行时)你已决定的类型使用。
答案 1 :(得分:4)
正如@Flater的回答所说,typeof
在这种情况下是有效的,除非你实例化这些对象时你不知道它们的类型。
因此,此解决方案适用于在运行时实例化的类型。如果您在编译时知道类型,它也会起作用,但它更复杂并且增加了复杂性。
解决方案是能够拥有您的类型的实例,以便在该实例上调用GetType()
,这将为您提供该对象的最低继承类型。
因此,要获取实例,要么更改函数并要求传递对象:
public static List<IndexRegistration> GetAll<T>(T instance) where T : AggregateRoot
{
return _register.FindAll(r => r.aggregateType == instance.GetType());
}
您create an instance at run-time,并检索该类型。这要求您将通用类型标记为parameterless constructor:
public static List<IndexRegistration> GetAll<T>() where T : AggregateRoot, New()
{
T instance = new T();
return _register.FindAll(r => r.aggregateType == instance.GetType());
}
答案 2 :(得分:2)
您的问题有类似的答案: when-and-where-to-use-gettype-or-typeof
通常typeof
运算符检查已知的编译时类型。所以在你的情况下它总是AggregateRoot
。
答案 3 :(得分:2)
要将T
本身与其所有继承者匹配,请使用IsAssignableFrom
反射方法,而不是类型对象比较:
public static List<IndexRegistration> GetAll<T>() where T : AggregateRoot
{
return _register.FindAll(r => typeof(T).IsAssignableFrom(r.aggregateType));
}