2012-05-07 4 views
3

Я создал интерфейс с одним методом, способным копировать содержимое одного объекта в другой объект того же типа (фактическая функциональность не имеет отношения к вопросу).Реализация общего метода

public interface IDeepClonable 
{ 
    void DeepClone<T>(T other); 
} 

У меня возникли проблемы с надлежащей реализацией.

То, что я действительно хотел бы, чтобы реализовать его, как это (там, где это находится внутри ClassA, который реализует IDeepClonable)

public void DeepClone<ClassA>(ClassA other) 
{ 
    this.A = other.A; 
} 

Однако это не работает, так как «другой» объект не опознан как экземпляр ClassA компилятором (почему?)

Это тоже не сработает, так как оно дает «ограничения для параметра типа T, должны соответствовать методу интерфейса (...).

public void DeepClone<T>(T other) where T : ClassA 
{ 
    this.A= other.A; 
} 

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

Я также могу решить это, превратив интерфейс в общий интерфейс, но затем это заставляет меня использовать этот общий интерфейс.

+0

BTW, 'DeepClone' является неправильным именем; метод под названием «DeepClone» должен вернуть копию. Вы должны называть это «DeepCloneFrom» или что-то в этом роде. – SLaks

+0

Я бы предположил, что если вы собираетесь делать глубокое клонирование, может быть полезно определить 'IImmutableClonable ' с помощью метода 'T AsImmutable()', 'IModifiableClone ' с методом 'U AsNewMutable()', и 'IFullClone ' наследует оба вышеперечисленных. Неизменяемые объекты просто вернутся из своих реализаций 'AsImmutable()'; 'AsNewMutable()' возвращает то, что позволяет, как минимум, любые методы мутации, требуемые контрактом U'. – supercat

ответ

5

Вы пытаетесь использовать CRTP.

Вам нужно написать

public interface IDeepClonable<out T> where T : IDeepClonable<T> 
{ 
    void DeepClone(T other); 
} 

public class ClassA : IDeepClonable<ClassA> { 
    void DeepClone(ClassA other) { ... } 
} 

Однако, это означает, что любой код, который использует IDeepClonable сам должен стать универсальным, который будет в конечном итоге становится громоздким.

Система CLR не достаточно богата, чтобы делать то, что вы действительно хотите.

+1

Одна из проблем с использованием CRTP заключается в том, что у вас может быть базовый класс, который включает в себя как клонируемые, так и не клонируемые производные, а не-клонируемые производные могут иметь клонируемые суб-производные. Добавляет ли ограничение к 'T', позволяет делать все, что невозможно сделать в его отсутствие? Кроме того, есть ли причина, чтобы не вводить параметр 'T' в ковариантный (' out') тип? – supercat

+0

@supercat: Я об этом не думал. – SLaks

0

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

public class ClassA : IDeepClonable 
{ 
    void DeepClone<T>(T other) { /* some implementation here */ } 
} 

что-то другое, чем это не будет работать.

Говорит, что, почему вам нужен этот вид сложности, вам не нужен родовые здесь, просто осуществить, как:

public interface IDeepClonable 
{ 
    void DeepClone(IDeepClonable other); 
} 

public class ClassA : IDeepClonable 
{ 
    void DeepClone(IDeepClonable other) 
    { 
     // just to be sure .... 
     if (other is ClassA) 
     { 
      var o = (ClassA)other; 
      this.A = o.A; 
     } 
    } 
} 
+0

Благодарим за отзыв. Однако я надеялся избежать такого рода реализации. – user981225