Использование Generics в C # во избежание дублирования кода - PullRequest
2 голосов
/ 14 ноября 2010

(язык c # с VS 2008)

У меня есть следующая проблема: существует ряд структур (предоставленных как есть от третьей стороны), которые реализуют определенные методы с одинаковыми сигнатурами.Я хотел бы обернуть эту структуру классами-обертками, которые реализуют определенный интерфейс, чтобы эти классы можно было обрабатывать единообразно.Пример:

interface AnInterface
{
    void DoSomething();
}

struct Struct1
{
    public void DoSomething();
}

class Struct1Wrapper : AnInterface
{
    private Struct1 m_struct;
    public override void DoSomething() // AnInterface implementation
    {
        m_struct.DoSomething();
    }
}

Обратите внимание, что метод Struct1 DoSomething является конкретным, в то время как Struct1Wrapper реализует его через интерфейс для облегчения обработки.

То же самое относится и к Struct2 и т. Д. - код StructXWrapperто же самое, за исключением того, что Struct1 заменен на StructX

. Я попытался использовать универсальные шаблоны во избежание дублирования кода:

class GenericStructWrapper<AStruct> : AnInterface
{
    private AStruct m_struct;

    public override void DoSomething() // AnInterface implementation
    {
        m_struct.DoSomething();
    }
}

Но это не сработает, так как компилятор не имеетПонятие о методе AStruct DoSomething().

Любая другая идея, как реализовать это без дублирования кода Struct1Wrapper?Возможно, есть какая-то макросоподобная функция или какое-то использование отражения?

Спасибо,

Ури Джамши.

Ответы [ 3 ]

6 голосов
/ 14 ноября 2010

Вы можете взять Action<AStruct> в конструкторе класса, который принимает метод.

Затем вы можете создавать экземпляры, такие как new GenericStructWrapper<Struct1>(s => s.DoSomething())

3 голосов
/ 14 ноября 2010

C # не безопасно поддерживает структурную типизацию (за исключением некоторых необычных контекстов), поэтому нет способа сделать это полностью безопасным без дублирования кода.Вы должны либо согласиться с техникой SLak, когда клиент запрашивает предоставить делегата (возможно, потребуется повторять одно и то же лямбда-выражение снова и снова), либо предположить , что базовые типы удовлетворятконтракт, содержащий метод public void DoSomething().

Переходя ко второму варианту, вот один из способов использования dynamic в C # 4:

public class StructWrapper: AnInterface
{
    private readonly dynamic m_struct;

    public StructWrapper(object myStruct)
    {
        m_struct = myStruct;
    }

    public void DoSomething()
    {
        m_struct.DoSomething();
    }
}

Теперь вы могли бы попытайтесь сделать этот класс универсальным, с типом базовой структуры, являющимся аргументом универсального типа, но это, вероятно, не сильно вам поможет, если вы также не захотите выполнить специфичную для структуры операций над упакованным типом.Вот пример этого с отражением и делегатами (совместим с C # 3):

public class StructWrapper<T> : AnInterface where T : struct
{   
    private readonly Action action;

    // deliberately exposed
    public T UnderlyingStruct { get; private set; }

    public StructWrapper(T underlyingStruct)
    {
        UnderlyingStruct = underlyingStruct;
        action = (Action)Delegate.CreateDelegate
                  (typeof(Action), underlyingStruct, "DoSomething");
    }

    public void DoSomething()
    {
        action();
    }
}

Обратите внимание, что вы можете смешивать и сочетать два метода, упомянутых выше, например, отражение, но без обобщений.

Использование:

AnInterface wrapper1 = new StructWrapper(new Struct1());
wrapper1.DoSomething();

StructWrapper<Struct1> wrapper2 = new StructWrapper<Struct1>(new Struct1());
wrapper2.DoSomething();
Struct1 s = wrapper2.UnderlyingStruct; // generics help here
s.SomeOtherMethod();
0 голосов
/ 14 ноября 2010

Для этого существует синтаксис:

class GenericStructWrapper<AStruct> : AnInterface where AStruct : AnInterface
{ 
    private AStruct m_struct; 

    public override void DoSomething() // AnInterface implementation 
    { 
        m_struct.DoSomething(); 
    } 
} 

Это говорит о том, что AStruct должен реализовать AnInterface

...