Приведение объекта к универсальному интерфейсу

StackOverflow https://stackoverflow.com/questions/222403

Вопрос

У меня есть следующий интерфейс:

internal interface IRelativeTo<T> where T : IObject
{
    T getRelativeTo();
    void setRelativeTo(T relativeTo);
}

и несколько классов, которые (должны) его реализовать, например:

public class AdminRateShift : IObject, IRelativeTo<AdminRateShift>
{
    AdminRateShift getRelativeTo();
    void setRelativeTo(AdminRateShift shift);
}

Я понимаю, что эти три не одно и то же:

IRelativeTo<>
IRelativeTo<AdminRateShift>
IRelativeTo<IObject>

но, тем не менее, мне нужен способ работы со всеми различными классами, такими как AdminRateShift (и FXRateShift, DetRateShift), которые должны реализовывать IRelativeTo. Допустим, у меня есть функция, которая возвращает AdminRateShift как объект:

IRelativeTo<IObject> = getObjectThatImplementsRelativeTo(); // returns Object

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

Это тривиальный пример, но я надеюсь, что он прояснит то, что я пытаюсь сделать.

Это было полезно?

Решение

Если я понимаю вопрос, то наиболее распространенным подходом было бы объявить неуниверсальный базовый интерфейс, т.е.

internal interface IRelativeTo
{
    object getRelativeTo(); // or maybe something else non-generic
    void setRelativeTo(object relativeTo);
}
internal interface IRelativeTo<T> : IRelativeTo
    where T : IObject
{
    new T getRelativeTo();
    new void setRelativeTo(T relativeTo);
}

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

void DoSomething<T>() where T : IObject
{
    IRelativeTo<IObject> foo = // etc
}

Если IRelativeTo<T> является аргументом для DoSomething(), то обычно вам не нужно указывать аргумент универсального типа самостоятельно - компилятор выведет его - т.е.

DoSomething(foo);

а не

DoSomething<SomeType>(foo);

У обоих подходов есть свои преимущества.

Другие советы

К сожалению, наследование не работает с генериками. Если ваша функция ожидает IRelativeTo, вы также можете сделать ее универсальной:

void MyFunction<T>(IRelativeTo<T> sth) where T : IObject
{}

Если я правильно помню, когда вы используете вышеописанную функцию, вам даже не нужно указывать тип, компилятор должен выяснить это на основе аргумента, который вы предоставляете.

Если вы хотите сохранить ссылку на один из этих объектов IRelativeTo внутри класса или метода (и вам все равно, что это за T), вам нужно снова сделать этот класс / метод универсальным.

Я согласен, это немного больно.

Если все, что вас волнует, это то, что IRelativeTo работает с объектами IObject, вам не нужно делать его универсальным:

interface IRelativeTo
 {
   IObject getRelativeTo();
   void setRelativeTo(IObject relativeTo)
 }

Реализующие классы могут все еще быть общими, однако:

abstract class RelativeTo<T>  : IRelativeTo where T : IObject
 {  
   public virtual T getRelativeTo() {return default(T);}

   public virtual void setRelativeTo(T relativeTo) {}

   IObject IRelativeTo.getRelativeTo() {return this.getRelativeTo(); }

   void IRelativeTo.setRelativeTo(IObject relativeTo) 
    { this.setRelativeTo((T) relativeTo);
    }
 }

class AdminRateShift :  RelativeTo<AdminRateShift>, IObject {}

Тогда вы можете сделать это:

  IRelativeTo irt = new AdminRateShift();
  IObject o = irt.getRelativeTo();
  irt.setRelativeTo(o);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top