2016-02-10 3 views
2

Я ищу лучший способ осуществить следующую ситуацию (.NET 3.5):C# Override и Новый В то же самое время

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() была реализовано в явном виде в обоих местах, это Wouldn Решить проблему: ((IGetThing<A>)new DerivedGetter<B, A>()).Get(), вызывая базовый метод, а не нужный производный метод.

Попытка реализовать как IGetThing, так и IGetThing в DerivedGetter вызывает исключение для компиляции. («DerivedGetter» не может реализовать как «IGetThing» и «IGetThing», потому что они могут объединиться для некоторых замен параметров типа)

Кроме того, попытки повторной реализации явной реализации BaseGetter в (IGetThing<A>.Get()) в DerivedGetter также обеспечивает исключение компиляции (The очевидный 'DerivedGetter.IGetThing < ...>. Get()': содержащий тип не реализует интерфейс 'IGetThing')

Целью является скрыть и переопределить базу Get() при использовании Derived.

У кого-нибудь есть идеи?

РЕДАКТИРОВАТЬ: Общее решение предпочтительно иметь возможность масштабировать до нескольких слоев производных классов.

Как в стороне, это только начало давать мне проблемы с компиляцией при изменении с .NET 4 на .NET 3.5.

ответ

2

Эта новая реализация учитывает ваши комментарии. Я не против сказать это - Это странно.

Первое, что вам нужно, - устранить статические общие ограничения, связанные с общими параметрами полученного геттера. Вы все еще можете проверить это, но это время выполнения.

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 { } 

Использование методов (так же, как и раньше!): вар а = новый DerivedGetter(); Console.WriteLine (a.Get() - Bb); var b = (IGetThing) a; Console.WriteLine (b.Get() - 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 

старой реализации ниже.


Я не думаю, что вы можете сделать это с помощью системы (только). Вы должны реализовать оба интерфейса либо через базовый класс, либо с производным классом.

Имея это в виду, я могу подумать о том, чтобы подойти к этой проблеме с помощью инъекции в поведении, которое вы хотите как защищенный член базового класса.

Нечто подобное: interface IGetThing { T Get(); }

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 
+0

На самом деле это не работает, если вы накладываете на '' IGetThing , вы получите стек переполнение, потому что BaseGetter 'Get()' в конечном итоге вызывает себя. – jdphenix

+0

Исправлено для этого случая. – jdphenix

+0

Жизнеспособное решение, приведенное выше, но не похоже, что оно будет масштабироваться для третьего (или более) уровня вывода. Например. Derived2Getter , где C: B, отбрасывание на DerivedGetter , попытка сделать Get(), чтобы вернуть тип C в виде бокса, как B. – Serge

0

После нескольких часов мышления, и сон хорошей ночи, я придумал с жизнеспособным решением, которое сохраняет оригинальный интерфейс и масштабируется до нескольких уровней наследования, не слишком сильно взрываясь.

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 
Смежные вопросы