3

我有一个容器类,它有一个泛型参数,它受限于某个基类。提供给泛型的类型是基类约束的子类。子类使用方法隐藏(新)来更改基类中方法的行为(不,我不能将其设为虚拟,因为它不是我的代码)。我的问题是没有调用“新”方法,编译器似乎认为提供的类型是基类,而不是子类,就好像我已经将它向上转换到基类一样。

显然,我在这里误解了一些基本的东西。我认为泛型where T: xxx是一种约束,而不是向上转型的类型。

这个示例代码基本上演示了我在说什么。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericPartialTest
{
    class ContextBase
    {
        public string GetValue()
        {
            return "I am Context Base: " + this.GetType().Name;
        }

        public string GetOtherValue()
        {
            return "I am Context Base: " + this.GetType().Name;
        }

    }

    partial class ContextSub : ContextBase
    {
        public new string GetValue()
        {
            return "I am Context Sub: " + this.GetType().Name;
        }
    }

    partial class ContextSub
    {
        public new string GetOtherValue()
        {
            return "I am Context Sub: " + this.GetType().Name;
        }
    }

    class Container<T> where T: ContextBase, new()
    {
        private T _context = new T();

        public string GetValue()
        {
            return this._context.GetValue();
        }

        public string GetOtherValue()
        {
            return this._context.GetOtherValue();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Simple");
            ContextBase myBase = new ContextBase();
            ContextSub mySub = new ContextSub();

            Console.WriteLine(myBase.GetValue());
            Console.WriteLine(myBase.GetOtherValue());
            Console.WriteLine(mySub.GetValue());
            Console.WriteLine(mySub.GetOtherValue());

            Console.WriteLine("Generic Container");
            Container<ContextBase> myContainerBase = new Container<ContextBase>();
            Container<ContextSub> myContainerSub = new Container<ContextSub>();

            Console.WriteLine(myContainerBase.GetValue());
            Console.WriteLine(myContainerBase.GetOtherValue());
            Console.WriteLine(myContainerSub.GetValue());
            Console.WriteLine(myContainerSub.GetOtherValue());


            Console.ReadKey();
        }
    }
}

编辑:

我想我的困惑来自于可以做到这一点

class SomeClass<T> where T: AnotherType, new()
{
    T foo = new T();     
}

即使我理解编译器会将其视为具有的接口,我也希望T如此。我假设即使接口是在编译时设置的,也会在运行时进行输入。该声明在这里似乎具有误导性,因为它确实在做TTAnotherTypeTTT foo

AnotherType foo = new T();

一旦我明白它并没有真正声明foo为 type T,就可以理解为什么new方法隐藏不起作用了。

这就是我要说的。

4

2 回答 2

3

声明new的方法与基类中具有相同名称/签名的方法没有关系(从编译器的角度来看)。这只是编译器允许您在派生类中定义与其基类层次结构中的方法共享签名的不同方法的方式。

现在,关于您的具体情况,请意识到泛型必须编译为一组字节码,而不管作为泛型参数提供的类型如何。因此,编译器只知道泛型类型 T 上定义的方法和属性 - 这将是您在泛型约束中指定的基本类型。编译器对派生类型中的方法一无所知new,即使您使用派生类型作为参数创建泛型类型的实例。因此,泛型类中的调用将始终转到基类型的方法。

关于 new/virtual/override 有很多困惑;看看这个 SO question - Jason 和 Eric 的回答非常好。Jon Skeet对类似问题的回答也可以帮助您理解为什么您的实现会如此行事。

有两种可能的方法可以解决此问题:

  1. 对泛型类中的派生类型(或接口)执行条件转换(基于运行时类型信息)。这会破坏封装并增加不希望的耦合。如果实施不当,它也很脆弱。
  2. 定义一个您在通用约束中使用的接口,该接口公开您关心的方法。如果您派生的代码不是您可以更改的,这可能是不可能的。
于 2010-04-15T22:53:15.040 回答
1

添加另一层 - 不是从您的第三方类继承您的泛型,而是从一个新类继承,该类又从第三方继承。在这个新类中,您可以将相关方法定义为新虚拟方法。如果您的所有代码从不直接引用第三方类,它应该可以工作

于 2010-04-15T23:02:17.680 回答