Как я могу иметь тип, который ссылается на любой объект, который реализует набор интерфейсов? - PullRequest
5 голосов
/ 14 ноября 2011

Как получить ссылку на тип, которая относится к любому объекту, который реализует набор интерфейсов?

Например, у меня может быть общий тип, подобный этому:

Java

public class Foo<T extends A & B> { }

C #

public class Foo<T> where T : A, B { }

Вот так должен быть универсальный универсальный тип. Однако я хотел бы просто иметь элемент данных, который ссылается на любой объект, который расширяет данный набор интерфейсов.

Пример:

public class Foo
{
    protected <? extends A, B> object;

    public void setObject(<? extends A, B> object)
    {
        this.object = object;
    }
}

Если возможно иметь такой синтаксис типа, как я могу сделать это как в Java, так и в C #?


Я понимаю, что могу просто создать другой интерфейс, который расширяет все желаемые интерфейсы. Однако я не считаю это оптимальным, поскольку в нем нет необходимости добавлять другой тип, единственная цель которого - обойти синтаксис. Конечно, это очень незначительная проблема, но с точки зрения элегантности это немного раздражает.

Ответы [ 5 ]

4 голосов
/ 14 ноября 2011

Моя Java стала немного ржавой за 2 года бездействия.

Вот мой подход на C #: (см. https://ideone.com/N20xU для полного рабочего образца)

public class Foo
{
    private IInterface1 _object; // just pick one

    public void setObject<T>(T obj)
        where T : IInterface1, IComparable<T>, IEtcetera
    {
        // you now *know* that object supports all the interfaces
        // you don't need the compiler to remind you
        _object = obj; 
    }

    public void ExerciseObject()
    { 
        // completely safe due to the constraints on setObject<T>
        IEtcetera itf = (IEtcetera) _object;

        // ....
    }
2 голосов
/ 14 ноября 2011

Насколько я знаю, вы не можете создать переменную с ограничениями на нее, вы можете создать только переменную данного типа. Тип имеет ограничения. Это означает, что вы должны определить тип, имеющий желаемые ограничения, а затем создать переменную с этим типом.

Это кажется мне логичным, и я не понимаю, почему вы находите "раздражающим" необходимость определять тип для того, что вам нужно.

1 голос
/ 14 ноября 2011

В C # вы можете использовать кортеж для хранения значения в виде суперпозиции :

public class Foo {
  private Tuple<IA, IB> junction;
  public void SetValue<T>(T value) where T : IA, IB {
    junction = Tuple.Create<IA, IB>(value, value);
  }
}

Вы также можете иметь специализированный класс дляприменить ограничение, что оба значения ссылаются на один и тот же объект:

public class Junction {
  public IA A { get; private set; }
  public IB B { get; private set; }
  private Junction() { }
  public static Junction Create<T>(T value) where T: IA, IB {
    return new Junction {
      A = value,
      B = value
    };
  }
}

public class Foo {
  private Junction junction;
  public void SetValue<T>(T value) where T : IA, IB {
    junction = Junction.Create(value);
  }
}

В Java подстановочный знак упростит вещи немного :

class Junction<E extends A & B> {
  private final E value;
  public Junction(E value) {
    this.value = value;
  }
  public E getValue() {
    return value;
  }
}

class Foo {
  private Junction<?> junction;
  public <E extends A & B> void setValue(E value) {
    junction = new Junction<E>(value);
  }
}

Или вы можете иметь псевдонимы к тому же значению (C #, но также применимо к Java):

public class Foo {
  private IA a;
  private IB b;
  public void SetValue<T>(T value) where T : IA, IB {
    a = value;
    b = value;
  }
}
0 голосов
/ 14 ноября 2011

Вот лучшее, что я мог придумать (но все еще не оптимальное решение)

public class Foo
{
    private TypeWrapper<IInterface1,IInterface2> _object;
    public void setObject<T>(T obj)
        where T : IInterface1, IInterface2
    {
        _object = new TypeWrapper<IInterface1, IInterface2>();
        _object.SetObject(obj);

        var val = _object.Get(h => h.c);
        Console.WriteLine(val);
        _object.Do(h => h.c = 25);
        val = _object.Get(h => h.c);
        Console.WriteLine(val);
        _object.Do(h => h.someF());
    }
}

public class TypeWrapper<TType, TTypeTwo>
{
    private Object m_Obj;
    public void SetObject<T>(T obj) where T : TType, TTypeTwo
    {
        m_Obj = obj;
    }

    public T Get<T>(Func<TType, T> action)
    {
        return (T)action((TType)m_Obj);
    }
    public T Get<T>(Func<TTypeTwo, T> action)
    {
        return (T)action((TTypeTwo)m_Obj);
    }

    public void Do(Action<TTypeTwo> action)
    {
        action((TTypeTwo)m_Obj);
    }

    public void Do(Action<TType> action)
    {
        action((TType)m_Obj);
    }
}

public class myClass : IInterface1, IInterface2 {
    public int t {get;set;}
    public int c {get;set;}
    public void someF() { Console.WriteLine("Fired"); }
    }
public interface IInterface1 {
    int t { get;set;} 
    void someF();
}
public interface IInterface2 { 
    int c { get;set; }
}

Вам придется работать с объектом с помощью методов Get и Do, но он будет работать с intellisense и генерировать ошибки времени компиляции, если изменятся интерфейсы.

0 голосов
/ 14 ноября 2011

Я не вижу проблем с указанием ограничений как частного интерфейса:

class Foo
{
    interface R : I1, I2 { }
    R _object;

    void setObject(R r) { _object = r; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...