Тема: System.ICloneable vs ICloneable<T>. Клонування об'єктів в C#.
Хочу створити інтерфейс ICloneable<...>, який буде містити один метод Clone(), і цей метод має повертати об'єкт, тип якого є таким як тип класу (або структури), який наслідує даний інтерфейс.
System.ICloneable запропонований Microsoft'ом мене не влаштовує, бо його метод повертає object.
Але мій інтерфейс буде наслідувати System.ICloneable (для підтримки логіки).
З використанням коваріації, мій інтерфейс мав би виглядати приблизно якось так:
public interface ICloneable<out T, ...> : System.ICloneable
where T : ICloneable<T, ...>, ...
...
{
new T Clone();
}
Найгарніший робочий варіант коду получивля наступний:
public interface ICloneable<out T> : System.ICloneable
where T : ICloneable<T>
{
new T Clone();
}
Але він не гарантує, що тип (тип-параметр) переданий в такий інтерфейс буде типом, який наслідує даний інтерфейс.
Приклад коду, де в ICloneable<T> передається не даний клас:
namespace MyNameSpace
{
/// <summary> Задає метод клонування. </summary>
/// <typeparam name="T"> Тип клонованого об'єкта. </typeparam>
public interface ICloneable<out T> : System.ICloneable
where T : ICloneable<T>
{
/// <summary> Повертає<returns> об'єкт, який є клоном даного об'єкта. </returns></summary>
new T Clone();
}
// Тут все добре
public abstract class CloneableClass : ICloneable<CloneableClass> // Тут тип-параметр правильний
{
/// <summary>
/// Реалізує <see cref="MyNameSpace.ICloneable`1.Clone()"/>,
/// в даному випадку "MyNameSpace.ICloneable<CloneableClass>.Clone()"
/// </summary>
public virtual CloneableClass Clone()
{
return (CloneableClass)MemberwiseClone(); // Ok
}
/// <summary> Реалізує <see cref="System.ICloneable.Clone()"/> </summary>
object System.ICloneable.Clone() { return Clone(); } // Ok
}
// Хочу, щоб компілятор казав, що неможна передавати CloneableClass в даному випадку, але
public class CrackCloneable : ICloneable<CloneableClass> // тут компілятор каже "все Ok"
{
public virtual CloneableClass Clone()
{
// throw new System.InvalidCastException();
return (CloneableClass)MemberwiseClone();
}
// throw new System.InvalidCastException();
object System.ICloneable.Clone() { return Clone(); }
}
}
Як описати ICloneable<...>, щоб інтерфейси, класи і структури (нащадки ICloneable<...>) могли передавати як тип-параметр тільки себе, або своїх нащадків ?
Якщо це неможливо, то чому ?
І взагалі цікаво почитати ваші міркування щодо цього.