Как использовать ICloneable<T>, когда T - это List<T>?

У меня есть следующее:

    public class InstanceList : List<Instance> {}

Я хотел бы сделать это клонируемым. Ниже приведен пример: почему нет ICloneable?

Я попробовал следующее:

    public interface ICloneable<T> : ICloneable Where T : ICloneable<T>
           {        new T Clone();    }

    public class InstanceList : List<Instance>, ICloneable<List<Instance>>  {}

Но я получаю ошибку компилятора. Сообщение об ошибке гласит, что List<Instance> должен быть конвертируемым в ICloneable<List<Instance>> для того, чтобы использовать параметр T в универсальном интерфейсе ICloneable<T>,

Что мне здесь не хватает?

4 ответа

Решение

Вы не можете сделать это, потому что вы не можете определить List<T> сам. Вы сможете сделать это только в том случае, если сможете List<T> из-за того, как вы ограничены ICloneable<T>, поскольку List<T> действительно не реализует ICloneable<T> вместо этого вам нужно будет иметь тип T быть InstanceList, который вы можете контролировать.

Вот как вы бы это реализовали:

public class InstanceList : List<Instance>, ICloneable<InstanceList>
{
    public InstanceList Clone()
    {
        // Implement cloning guts here.
    }

    object ICloneable.Clone()
    {
        return ((ICloneable<InstanceList>) this).Clone();
    }
}

public class Instance
{

}

public interface ICloneable<T> : ICloneable where T : ICloneable<T>
{
    new T Clone();
}

Конечно, есть другая альтернатива, которую вы могли бы сделать. Вы можете немного расширить свои дженерики, чтобы создать CloneableList<T> тип:

public class CloneableList<T> : List<T>, ICloneable<CloneableList<T>>
{
    public CloneableList<T> Clone()
    {
        throw new InvalidOperationException();
    }

    object ICloneable.Clone()
    {
        return ((ICloneable<CloneableList<T>>) this).Clone();
    }
}

public interface ICloneable<T> : ICloneable where T : ICloneable<T>
{
    new T Clone();
}

И если вы действительно хотите стать модным, создайте что-то, что ограничивает T до ICloneable. Тогда вы могли бы реализовать ICloneable в классе Instance и все, что вы хотите включить в ICloneable<T> список, таким образом, рассматривая каждый CloneableList<T> точно так же, избегая другой реализации ICloneable<T> для каждого клонируемого списка, который вы хотите создать.

public class CloneableList<T> : List<T>, ICloneable<CloneableList<T>> where T : ICloneable
{
    public CloneableList<T> Clone()
    {
        var result = new CloneableList<T>();
        result.AddRange(this.Select(item => (T) item.Clone()));
        return result;
    }

    object ICloneable.Clone()
    {
        return ((ICloneable<CloneableList<T>>) this).Clone();
    }
}

public interface ICloneable<T> : ICloneable where T : ICloneable<T>
{
    new T Clone();
}

Проблема в вашем общем ограничении where T : IClonable<T>, Потому что вы "создаете экземпляр" своего интерфейса как ICloneable<List<Instance>>, List<Instance> твой Tи поэтому общее ограничение переводится в where List<Instance> : IClonable<List<Instance>>, List<Instance> не выполняет это ограничение.

Возможно, вы пытаетесь сделать что-то вроде этого:

public interface ICloneableList<T> : ICloneable where T : ICloneable
{
}

Чтобы добавить к другим хорошим ответам уже там - когда вы клонируете, вы ожидаете получить идентичную копию обратно, верно? Так что вместо:

public class InstanceList : List<Instance>, ICloneable<List<Instance>>  {}

Не должно ли это быть на самом деле:

public class InstanceList : List<Instance>, ICloneable<InstanceList>  {}

Таким образом, вы также не получите ошибок компилятора.

Я не думаю, что вы действительно можете делать то, что вы хотите. Хотя полезно не требовать аргумент типа ICloneable для реализации ICloneable, я не думаю, что класс List можно очень хорошо расширить для поддержки клонирования, поскольку он не предоставляет никаких средств отсоединения. или дублирование массива, который содержит все элементы данных, не позволяет подклассу обращаться к этому массиву и не позволяет подклассу переопределять достаточно виртуальных методов, чтобы сделать массив неактуальным. Хотя методы клонирования должны начинаться с использования MemberwiseClone (чтобы гарантировать, что клонированный объект имеет тот же тип, что и исходный), не будет гарантированного способа заставить вновь клонированный список создать новый массив для хранения его объектов, не нарушая старый один.

Самое близкое, что я могу предложить для того, чтобы делать то, что вам нужно, это определить ICloneableList, который наследуется от IList и ICloneable>, и определить класс CloneableList, который реализует это путем переноса списка. Клонирование CloneableList должно создать новый List с элементами, скопированными из старого, что можно сделать с помощью соответствующего конструктора для нового List.

Другие вопросы по тегам