如何在C#

时间:2017-09-11 08:24:26

标签: c# generics

我有一个通用的方法

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的子类型)?

4 个答案:

答案 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));
}