本文介绍了如何对通用约束类使用方法隐藏(新)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个具有通用参数的容器类,该类只限于某些基类.提供给泛型的类型是基类约束的子类.子类使用方法隐藏(新)来更改基类中方法的行为(不,我不能将其虚拟化,因为它不是我的代码).我的问题是新"方法没有被调用,编译器似乎将提供的类型视为基类,而不是子类,好像我已将其转换为基类一样.

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.

很明显,我在这里误解了一些基本知识.我以为通用的其中T:xxx 是一个约束,而不是强制类型.

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

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

I guess my confusion comes from that one can do this

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

我希望 T T ,尽管我知道编译器会认为 T 具有 AnotherType 的界面.我假设即使在编译时设置了 T 的接口,在运行时也会键入 T . T foo 声明在这里似乎具有误导性,因为它确实在做

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

一旦我了解它并没有真正将 foo 声明为 T 类型,那么可以理解为什么隐藏 new 方法无效的原因

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

这就是我要说的.

推荐答案

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

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.

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

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.

关于新/虚拟/覆盖有很多困惑; 请使用-Jason和Eric的回答很棒.乔恩·斯凯特(Jon Skeet)对类似问题的回答也可能有助于您理解为什么实现的行为方式.

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. 对泛型类中的派生类型(或接口)执行基于条件的强制转换(基于运行时类型信息).这破坏了封装并且增加了不希望的耦合.如果实施不当,它也很脆弱.
  2. 定义在通用约束中使用的接口,该接口公开了您关心的方法.如果您衍生的代码不是您可以更改的代码,则可能无法实现.
  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.

这篇关于如何对通用约束类使用方法隐藏(新)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 04:41