成员查找C#中的泛型约束?

时间:2014-03-24 10:07:35

标签: c# generics

我有这个简单的代码:(简化)

 class Program
    {
        static void Main(string[] args)
        {
            var s1 = new Student();
            var s2 = new Student();
            myGenericClass<Student> Mgc = new myGenericClass<Student>();
            Mgc.Eq(s1,s2);
        }
    }

    class myGenericClass<T> where T : Person
    {
        public void Eq(T t1, T t2)
        {
           Console.WriteLine(t1.Equals(t2));
        }
    }

    class Person
    {
         public  bool Equals(Person p)
        {
            return true;
        }
    }

    class Student : Person
    {
         public bool Equals(Student obj)
        {
            return true;
        }
    }

调用的方法是Person's,这是问题:

myGenericClass初始化为Student,因此T为学生。那里还有一个约束:where T : Person - 这意味着人should be a base class。 (学生确实做到了这一点)

问题:

1)这种行为的原因是否与泛型相关?

2)如果是这样,为什么仍然会运行Person的方法而不是Student?什么阻止它与学生打交道?

编辑,我也看到了这个in the spec

3 个答案:

答案 0 :(得分:1)

如果通过“此行为”表示它在Person上调用方法,则表示不是,这是正常的OOP。

为了让您的代码根据实际使用的T调用“正确的”方法,您需要在基类中创建该方法virtual,然后override在后代:

class Person
{
    public virtual bool Equals(Person p)
    {
        return true;
    }
}

class Student : Person
{
    public override bool Equals(Person obj)
    {
        return true;
    }
}

但是也要观察其他内容,为了Equals中的Student覆盖Person中的Person,它还需要一个Person参数。< / p>

请注意,泛型并不意味着“弄清楚在运行时调用哪个方法”,它仍然是编译器在编译时确定调用哪些方法,并且在编译泛型类时它可以看到的是Student类,因此无法确定void Main() { Base o = new Derived(); o.Test1(); o.Test2(); } public class Base { public void Test1() { Debug.WriteLine("Base.Test1"); } public virtual void Test2() { Debug.WriteLine("Base.Test2"); } } public class Derived : Base { public void Test1() { Debug.WriteLine("Derived.Test1"); } public override void Test2() { Debug.WriteLine("Derived.Test2"); } } 的潜在用法。

您可以使用这个简单的LINQPad程序验证此行为:

Base.Test1
Derived.Test2

输出:

{{1}}

以及此警告(您可能在代码中也有此警告):

  

警告:'UserQuery.Derived.Test1()'隐藏继承的成员'UserQuery.Base.Test1()'。如果想要隐藏,请使用new关键字。

答案 1 :(得分:0)

myGenericClass<T>.Eq方法不了解从Person派生的类;它所知道的只是TPersonPersonEquals(Person p)方法。由于方法调用在编译时被解析,因此调用它是Person.Equals(Person p),而不管派生类型中声明的更具体的方法。

但是你可以这样做:

class myGenericClass<T> where T : Person, IEquatable<T>
{
    public void Eq(T t1, T t2)
    {
       Console.WriteLine(t1.Equals(t2));
    }
}

class Person : IEquatable<Person>
{
    public  bool Equals(Person p)
    {
        return true;
    }
}

class Student : Person, IEquatable<Student>
{
    public  bool Equals(Student s)
    {
        return true;
    }
}

在这种情况下,myGenericClass<T>.Eq将使用Equals中的IEquatable<T>方法,最终会调用Student实现。但是,它要求从Person继承的所有类都实现IEquatable<T>

答案 2 :(得分:0)

要执行您需要的操作,您需要添加如下界面:

static void Main(string[] args)
{
  var s1 = new Student();
  var s2 = new Student();
  myGenericClass<Student> Mgc = new myGenericClass<Student>();
  Mgc.Eq(s1, s2);

  var p1 = new Person();
  var p2 = new Person();
  myGenericClass<Person> Pgc = new myGenericClass<Person>();
  Pgc.Eq(p1, p2);
}

class myGenericClass<T> where T : IFoo<T>
{
   public void Eq(T t1, T t2)
   {
      Console.WriteLine(t1.Equals(t2));
   }
}

interface IFoo<T>
{
    bool Equals(T t);
}

class Person : IFoo<Person>
{
   public bool Equals(Person p)
   {
       return true;
   }
}

class Student : IFoo<Student>
{
    public bool Equals(Student obj)
   {
       return false;
   }
}

这将打印False然后True

我希望这会有所帮助。