1 Востаннє редагувалося leofun01 (29.10.2015 13:17:17)

Тема: 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&lt;CloneableClass&gt;.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<...>) могли передавати як тип-параметр тільки себе, або своїх нащадків ?
Якщо це неможливо, то чому ?
І взагалі цікаво почитати ваші міркування щодо цього.

2 Востаннє редагувалося leofun01 (10.11.2015 13:09:57)

Re: System.ICloneable vs ICloneable<T>. Клонування об'єктів в C#.

В пошуках досконалого ICloneable'а, я дійшов висновку, що в C# його неможливо зробити таким як я хочу (принаймні покищо).
Рішенням цієї проблеми могло би стати використання ключових слів як типів у наслідуванні.
Наприклад код міг би виглядати так:

public interface ICloneable<out T> : System.ICloneable
    where T : ICloneable<T>, this
{
    new T Clone();
}

або так:

public interface ICloneable : System.ICloneable
{
    new this Clone();
}

Але C#-компілятор зригує кров'ю, коли я пишу таке збочення.