【问题标题】:C# Override and New At Same TimeC# 覆盖和同时新建
【发布时间】:2016-02-10 00:29:17
【问题描述】:

我正在寻找实现以下情况的最佳方法(.NET 3.5):

interface IGetThing<T>
{ 
  T Get();
}

class BaseGetter<A> : IGetThing<A> where A : new()
{
   public virtual A Get()
   {
     return new A();
   }
}

class DerivedGetter<B, A> : Base, IGetThing<B> where B : A, new() where A : new()
{
   public override A Get()
   {
       return Get(); //B version
   }

   public new virtual B Get()
   {
       return new B();
   }
}

我评估过 This one 之类的帖子,但我看不到它提供的等效解决方案。

我看到有人建议我使用显式接口实现来做类似的事情,但我看不出这如何解决继承问题:

如果在这两个地方都显式实现了 Get(),它不会解决以下问题:((IGetThing&lt;A&gt;)new DerivedGetter&lt;B, A&gt;()).Get() 调用基方法,而不是所需的派生方法。

尝试在 DerivedGetter 中同时实现 IGetThing 和 IGetThing 会导致编译异常。 ('DerivedGetter' 不能同时实现 'IGetThing' 和 'IGetThing' 因为它们可能会统一用于某些类型参数替换)

此外,尝试在 DerivedGetter 中重新实现 BaseGetter 的显式实现 (IGetThing&lt;A&gt;.Get()) 也会提供编译异常(明显的 'DerivedGetter.IGetThing<...>.Get()': contains type does not implement interface ' IGetThing')

目标是在使用 Derived 时隐藏和覆盖 base 的 Get()。

有人有什么想法吗?

编辑:整体解决方案最好能够扩展到多层派生类。

顺便说一句,这只是在我从 .NET 4 更改为 .NET 3.5 时才开始出现编译问题。

【问题讨论】:

    标签: c# .net .net-3.5 overriding


    【解决方案1】:

    这个新的实现将您的 cmets 考虑在内。我不介意这样说 - 这很奇怪

    第一件事 - 你必须取消派生的 getter 的泛型参数相关的静态泛型约束。你仍然可以检查这个,但它是一个运行时间。

    interface IGetThing<T>
    {
        T Get();
    }
    
    class BaseGetter<A> : IGetThing<A> where A : new()
    {
        public BaseGetter()
        {
            var generics = this.GetType().GetGenericArguments();
    
            for (var i = 0; i < generics.Length - 1; i++)
            {
                if (generics[i].BaseType != generics[i+1])
                {
                    throw new ArgumentException(
                        string.Format("{0} doesn't inherit from {1}", 
                        generics[i].FullName, 
                        generics[i + 1].FullName));
                }
            }
    
            getters = new Dictionary<Type, Func<object>>();
            getters.Add(typeof(A), () => new A());
        }
    
        protected readonly IDictionary<Type, Func<object>> getters; 
    
        protected object Get(Type type)
        {
            var types = type.GetGenericArguments();
    
            return getters[types[0]]();
        }
    
        public virtual A Get()
        {
            return (A) Get(this.GetType());
        }
    }
    
    class DerivedGetter<B, A> : BaseGetter<A>, IGetThing<B>
        where B : new() where A : new()
    {
        public DerivedGetter()
        {
            getters.Add(typeof(B), () => new B());
        }
    
    
        B IGetThing<B>.Get()
        {
            return (B) Get(this.GetType());
        }
    }
    
    class Derived2Getter<C, B, A> : DerivedGetter<B, A>, IGetThing<C>
        where C : new() where B : new() where A : new()
    {
        public Derived2Getter()
        {
            getters.Add(typeof(C), () => new C());
        }
    
        C IGetThing<C>.Get()
        {
            return (C) Get(this.GetType());
        }
    }
    
    class Aa { }
    
    class Bb : Aa { }
    
    class Cc : Bb { }
    
    class Dd { }
    

    方法的使用(和以前一样!): var a = new DerivedGetter(); Console.WriteLine(a.Get() is Bb); var b = (IGetThing)a; Console.WriteLine(b.Get() is Bb);

    var c = new Derived2Getter<Cc, Bb, Aa>();
    Console.WriteLine(c.Get() is Cc);
    var d = (IGetThing<Bb>)c;
    Console.WriteLine(d.Get() is Cc);
    var e = (IGetThing<Aa>)c;
    Console.WriteLine(e.Get() is Cc);
    
    var f = new DerivedGetter<Dd, Aa>();
    

    输出:

    True
    True
    True
    True
    True
    
    Unhandled Exception: System.ArgumentException: 
    ConsoleApplication16.Dd doesn't inherit from 
    ConsoleApplication16.Aa
    

    下面的旧实现。


    我认为您不能使用(仅)类型系统来做到这一点。您必须通过基类或派生类来实现这两个接口。

    考虑到这一点,我可能会考虑通过将您想要的行为作为基类的受保护成员注入来解决此问题。

    类似这样的: 接口 IGetThing { 获取(); }

    class BaseGetter<A> : IGetThing<A> where A : new()
    {
        protected IGetThing<A> Getter { get; set; }
    
        public virtual A Get()
        {
            return Getter == null ? new A() : Getter.Get();
        }
    }
    
    class DerivedGetter<B, A> : BaseGetter<A>, IGetThing<B> where B : A, new() where A : new()
    {
        public DerivedGetter()
        {
            Getter = this;
        }
    
        public override A Get()
        {
            return new B();
        }
    
        B IGetThing<B>.Get()
        {
            return (B) Get();
        }
    }
    
    class Aa { }
    
    class Bb : Aa { }
    

    运行时,

    var a = new DerivedGetter<Bb, Aa>();
    Console.WriteLine(a.Get() is Bb);
    var b = (IGetThing<Aa>)a;
    Console.WriteLine(b.Get() is Bb);
    

    输出:

    True
    True
    

    【讨论】:

    • 实际上,如果将其转换为IGetThing&lt;A&gt;,这将不起作用,因为 BaseGetter Get() 最终会调用自身。
    • 针对这种情况修复。
    • 在上述情况下可行的解决方案,但它看起来不能扩展到第三(或更高)级别的推导。例如。 Derived2Getter where C : B,转换为 DerivedGetter,尝试执行 Get() 以返回装箱为 B 的 C 类型。
    • 好吧,超越不是您问题的原始部分,尽管这是一个好主意。让我考虑一下,然后回复你。如果你不介意,你能用这些新信息更新你的问题吗?或者也许解释你在这里的用例,因为它一开始有点复杂。我认为可能存在一些 X-Y 问题。
    • 假设没有其他人在第二天提出一个非常简单的“duh”解决方案(我非常怀疑),这将被标记为接受。我向你保证,这 100% 可能是 XY 问题。我正在尝试定义一个严格的ICopyable&lt;X&gt; 接口(X 指的是实现类本身,方法X Copy()void CopyMemebers(X other)),而Copy() 方法属于上述模式。抛开 XY 不谈,这是否可以在没有反射的情况下在语法上完成的实际问题仍然让我产生疑问,因为它并不是一个真正不合理的情况 IMO。
    【解决方案2】:

    经过数小时的思考和一夜好眠,我想出了一个可行的解决方案,该解决方案保留了原始界面,并且可以扩展到多个继承级别而不会爆炸太多。

    interface IGetThing<T>
    { 
      T Get();
    }
    
    class BaseGetter<A> : IGetThing<A> 
      where A : new()
    {
      public A Get()
      {
        A result;
        GetInternal(out result);
        return result;
      }
    
      protected virtual void GetInternal(out A target)
      {
        target = new A();
      }
    }
    
    class DerivedGetter<B, A> : BaseGetter<A>, IGetThing<B> 
      where B : A, new() 
      where A : new()
    {
      public new B Get()
      {
        B result;
        GetInternal(out result);
        return result;
      }
    
      protected override void GetInternal(out A target)
      {
        target = Get();
      }
    
      protected virtual void GetInternal(out B target)
      {
        target = new B();
      }
    }
    
    class Derived2Getter<C, B, A> : DerivedGetter<B, A>, IGetThing<C>
      where C : B, new()
      where B : A, new()
      where A : new()
    {
      public new C Get()
      {
        C result;
        GetInternal(out result);
         return result;
      }
    
      protected override void GetInternal(out B target)
      {
        target = Get();
      }
    
      protected virtual void GetInternal(out C target)
      {
        target = new C();
      }
    }
    

    实施贯穿时:

    class Aa { }
    
    class Bb : Aa { }
    
    class Cc : Bb { }
    
    class Program
    {
      static void Main(string[] args)
      {
        BaseGetter<Aa> getter = new DerivedGetter<Bb, Aa>();
        Console.WriteLine("Type: " + getter.Get().GetType().Name);
        getter = new Derived2Getter<Cc, Bb, Aa>();
        Console.WriteLine("Type: " + getter.Get().GetType().Name);
      }
    }
    

    控制台输出是

    Type: Bb
    Type: Cc
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-16
      • 2020-05-11
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      • 2012-07-18
      相关资源
      最近更新 更多