Как использовать ICloneable <T>, когда T равно List <T>? - PullRequest
4 голосов
/ 02 августа 2011

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

    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 ]

4 голосов
/ 02 августа 2011

Вы не можете сделать это, потому что вы не можете определить List<T> самостоятельно.Вы сможете сделать это только в том случае, если сможете объявить свой собственный List<T> из-за способа ограничения ICloneable<T>.Поскольку List<T> действительно не реализует ICloneable<T>, вам нужно будет иметь тип T be 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();
}
2 голосов
/ 02 августа 2011

Проблема в вашем общем ограничении 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
{
}
1 голос
/ 02 августа 2011

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

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

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

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

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

0 голосов
/ 22 сентября 2011

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

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

...