为什么泛型类型不是正确的类型?

5

我有两个类,BaseClassPersonPerson 类继承自 BaseClass。然后我使用以下通用方法。

    public static PropertyInfo GetProperty<T>(T item, Func<PropertyInfo, bool> predicate)
    {
        return GetProperty(typeof(T), predicate);
    }

BaseClass中,我有一个方法,调用GetProperty

public class BaseClass
{
    public void DoSomething()
    {
        var property = GetProperty(new Person(), (property) => p.Name == "Name");
    }
}

我会在单元测试中调用这个方法。

var person = new Person();
person.DoSomething();

当使用typeof(T)时,返回BaseClass。如果我使用item.GetType(),则返回Person。如果在调试器中检查它们,T的类型是BaseClass,而item的类型是Person。为什么它不推断出Ttypeof(Person),当它已经知道item是一个人?

编辑

对不起,我的例子是错误的,当我调用GetProperty时,我传递了this

public class BaseClass
{
    public void DoSomething()
    {
        var property = GetProperty(this, (property) => p.Name == "Name");
    }
}

1
如果通用方法由编译器关闭,且T=BaseClass,那么程序仍将编译和运行,并且您将会看到您所描述的输出。一般情况下,应尽可能使用GetType(),因为您将始终获得对象的具体类型。 - Steve Guidi
1
我不知道,但当我编译它时,它会正确地推断出“Person”。将其作为完整代码发布。 - IS4
1
BaseClass 的范围内,typeof(this) 总是会返回 BaseClass。然而,this.GetType() 被重载了并返回了 Person - Steve Guidi
啊,好的,我以为 typeof(this) 会知道 this 是一个子元素。谢谢。 - Johnathon Sullinger
现在您已经更新了问题,情况变得清晰了。在 DoSomething 中,您传递了 this,它是一个类型为 BaseClass 的对象。因此编译器正确推断类型为 BaseClass - IS4
1个回答

2
这个推断问题的原因是通用推断发生在编译时,因此与手动指定所需类型相同。如果您在调用中切换到明确声明 <Person>,它是否会在编译期间抛出错误?
解决这个问题的一种方法是确保传递的变量(注意:不是对象!)显式地属于 Person 类,就像您的代码中一样。另一种方法是通过使用 dyanmic 对象来强制在运行时进行通用推断,如下所示:
GetProperty(this as dynamic, (prop) => prop.Name == "Name");

通过将 this 转换为动态类型,它在运行时使用 this 的确切类型调用 GetProperty<dynamic>。这种方法的问题是与其他对象相比,动态类型非常缓慢。
如果您的模型严格只有一个继承级别,您还可以利用静态多态性来处理泛型参数。因此:
public class BaseClass<TSelf> where TSelf : BaseClass<TSelf>

public sealed class Model : BaseClass<Model>

这种方式中,您可以在参数位置上使用TSelf代替T,并且它会完全正确。但是,这种方法的问题在于它严格限制了您只能使用单继承层次结构,因为从Model继承的任何内容都会回到最初的问题,并被视为Model,因为它无法覆盖其基类使用的通用参数。

假设您的GetProperty函数使用反射来检查属性,则可能希望考虑传递一个Type对象,而不是使用一个泛型,然后使用typeof(T),因为.GetType()更准确。


我的问题是有30多个模型继承自我的基类,我的视图模型调用了该基类方法。在基类方法中添加强类型的泛型将是冗余的。我不确定你的第二个建议,我应该在从我的视图模型调用该方法时提供它吗?this.SelectedModel.DoSomething<dynamic>();? - Johnathon Sullinger
回答你关于明确声明 <Person> 的问题,它不会抛出异常。在我的单元测试中,我有 PersonFixture : BaseClass 并调用 PersonFixture.DoSomething()。当我进行调试时,typeof(T)Person - Johnathon Sullinger
我编辑了我的 OP。我将 this 传递到方法中,并且 this 带有在基类中发生的调用。当我从基类中调用该方法时,我是否应该只传递 this.GetType() - Johnathon Sullinger
总结一下我更新后的回答中的最后一段,是的,你应该完全这样做,但我也包括了其他几个选项。 - David
谢谢提供的选项,这将解决我的问题。由于它位于缓存池中,我可能会保留基类的方法签名和调用方式,并更新typeof(T)以使用GetType()。我不想让用户在使用缓存时记得始终传递GetType() :) - Johnathon Sullinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接