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

显然我在这里误解了一些基本的东西。我以为通用的 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 成为 T 即使我了解编译器会查看 T 作为有 AnotherType的界面。我假设输入的是 T 即使接口 T 在编译时设置。这 T foo 声明在这里似乎具有误导性,因为它确实在做

AnotherType foo = new T();

当我明白这并不是真正的宣告 foo 作为类型 T, ,这是可以理解的,为什么 new 方法隐藏不起作用。

这就是我要说的。

有帮助吗?

解决方案

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

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

关于 new/virtual/override 有很多混淆; 采取一个 看看这个问题 - 杰森和埃里克的回答非常好。 乔恩·斯基特的回答 类似的问题也可以帮助您理解为什么您的实现会如此运行。

您可以通过两种方法来解决此问题:

  1. 执行条件转换 (基于运行时类型信息)到泛型类中的派生类型(或接口)。这会破坏封装并增加不需要的耦合。如果实施不当,它也很脆弱。
  2. 定义一个接口 您在通用约束中使用它来公开您关心的方法。如果您无法更改派生的代码,则这可能是不可能的。

其他提示

我觉得这太问题我问可以帮助你。见乔恩斯基特的回答有它为什么是不可能的。

添加其他层 - 继承你一般不会从您的第三方类,但来自一类新的从第三方转继承。在这个新的类,你可以定义的问题作为新的虚拟方法。如果你所有的代码永远不会直接引用的第三部分类,它应该工作

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top