如何将方法隐藏(新)与通用约束类一起使用

发布于 2024-08-29 06:04:03 字数 2836 浏览 6 评论 0原文

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

显然我在这里误解了一些基本的东西。我认为通用 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();     
}

而且我预计 TT 即使我知道编译器会查看 < code>T 具有 AnotherType 的接口。我假设即使 T 的接口是在编译时设置的,T 的类型也会在运行时发生。 T foo 声明在这里似乎具有误导性,因为它确实在做

AnotherType foo = new T();

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

这就是我要说的。

I have a container class that has a generic parameter which is constrained to some base class. The type supplied to the generic is a sub of the base class constraint. The sub class uses method hiding (new) to change the behavior of a method from the base class (no, I can't make it virtual as it is not my code). My problem is that the 'new' methods do not get called, the compiler seems to consider the supplied type to be the base class, not the sub, as if I had upcast it to the base.

Clearly I am misunderstanding something fundamental here. I thought that the generic where T: xxx was a constraint, not an upcast type.

This sample code basically demonstrates what I'm talking about.

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();
        }
    }
}

Edit:

I guess my confusion comes from that one can do this

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

And I expected T to be T even though I understand the compiler would view T as having AnotherType's interface. I assumed the typing of T would happen at run-time even if the interface of T was set at compile time. The T foo declaration seems misleading here because it is really doing

AnotherType foo = new T();

Once I understand that it is not really declaring foo as type T, it is understandable why the new method hiding wouldn't work.

And that's all I have to say about that.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

唠甜嗑 2024-09-05 06:04:03

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

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

关于 new/virtual/override 有很多困惑;看一下看看在这个SO问题 - Jason和Eric的回答非常好。 Jon Skeet 对类似问题的回答也可能会帮助您理解为什么您的实现的行为就像它所做的那样。

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

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

Methods declared new have no relation (from the compiler's perspective) to methods with the same name/signature in the base class. This is simply the compiler's way of allowing you to define different methods in derived classes that share a signature with a method in their base class heirarchy.

Now, with regard to your specific case, realize that generics have to compile to a single set of bytecode regardless of the types that are supplied as generic parameters. As a result, the compiler only knows about the method and properties that are defined on the generic type T - that would be the base type you specify in the generic constraint. The compiler knows nothing about the new methods in your derived type, even if you create an instance of a generic type with the derived type as the parameter. Therefore calls in the generic class will always go to the methods of the base type.

There's a lot of confusion about new/virtual/override; take a look at this SO question - Jason and Eric's answers are excellent. Jon Skeet's answer to a similar question may also help you understand why your implementation behaves the way it does.

There are two possible ways for you to work around this issue:

  1. Perform a conditional cast (based on runtime type information) to the derived type (or an interface) in your generic class. This breaks encapsulation and adds undesirable coupling. It's also fragile if implemented poorly.
  2. Define an interface that you use in your generic constraint that exposes the methods you care about. This may not be possible if the code you are deriving from is not something you can change.
清风不识月 2024-09-05 06:04:03

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

Add another layer - inherit your generic not from your third party class but from a new class which in turn inherits from the third party. In this new class you can define the method in question as new virtual. If all your code never references the third part class directly, it should work

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文