Как обходить отсутствующий ICloneable интерфейс при переносе библиотеки .NET в PCL?
Я переношу существующую библиотеку классов .NET в переносимую библиотеку классов. Библиотека .NET широко использует интерфейс ICloneable, который не включен в переносное подмножество.
Как правило, я сталкиваюсь с определениями классов, как это в библиотеке классов .NET:
public class Foo<T> where T : ICloneable
{
public void Bar(T item) {
var x = item.Clone();
...
}
}
Что я могу сделать, чтобы успешно перенести этот код в переносимую библиотеку классов? Должен ли я переписывать вызовы метода Clone
, или существует ли менее навязчивое обходное решение?
Я не могу просто удалить ограничение типа generic where T : ICloneable
, потому что метод Bar
не будет компилироваться.
Я мог бы написать заменяющий интерфейс для использования вместо ICloneable
в портированном коде:
public interface IPCLCloneable {
object Clone();
}
Это будет работать до тех пор, пока я только создаю экземпляр Foo<T>
с классами, реализующими IPCLCloneable
, но он не будет работать, например, с типами из основных библиотек, которые реализуют ICloneable
, например Array:
var x = new Foo<int[]>(); // Compilation error in PCL library
(Для полноты следует отметить, что интерфейс ICloneable
явно не реализован в переносных библиотеках подмножеств, так как он не существует, но метод object Clone()
существует в классе переносного подмножества Array
, и, следовательно, для реализаций массива, таких как int[]
.)
Какие еще параметры у меня есть?
Ответы
Ответ 1
ICloneable
- это пример учебника, в котором мы допустили ошибку при разработке API. Будь то глубокая или мелкая копия, undefined, а в Руководстве по дизайну Framework теперь рекомендуется не использовать его.
Тем не менее, если вам все равно нужно использовать его, вы, вероятно, можете сделать это из PCL, указав интерфейс (с таким же именем) в PCL, а когда вы работаете на платформе с ICloneable, замените сборка с определением ICloneable с тем, у кого есть тип, переходящий к "реальной" версии. См. Мой ответ здесь, чтобы немного подробнее об этом: Есть ли способ реализовать IValidatableObject в проекте библиотеки портативных классов?
Ответ 2
Я не знаю, допустимо ли это:
public class Foo<T>
{
// since we can't check T at compile-time anymore (no constraint), we do this
static Foo()
{
if (!typeof(IPclCloneable).IsAssignableFrom(typeof(T)) && !typeof(T).IsArray)
throw new ArgumentException("Type must have Clone method", "T");
}
public void Bar(T item)
{
var x = item.Clone();
...
}
}
public static class YourExtensions
{
public static object Clone(this object obj)
{
if (obj == null)
throw new ArgumentNullException("obj");
var objIPclCloneable = obj as IPclCloneable;
if (objIPclCloneable != null)
return objIPclCloneable.Clone();
var objArray = obj as Array;
if (objArray != null)
return objArray.Clone();
throw new ArgumentException("Type of 'this' must have Clone method", "obj");
}
}
public interface IPclCloneable
{
object Clone();
}
Ответ 3
Аналогичная проблема имеет инфраструктура сравнения .NET. Вы иногда хотите сортировать объекты, которые не реализуют IComparable
, но вы можете внешне указать IComparer<T>
для алгоритма сортировки. Здесь вы можете сделать что-то похожее: Создайте интерфейс ICloneProvider<T>
, который поддерживает клонирование объекта данного типа. Вы должны убедиться, что весь код, который нужно клонировать, имеет подходящий экземпляр этого интерфейса.
Ответ 4
Начиная с версии .NET Standard 2.0 интерфейс ICloneable
возвращается и должен поддерживаться всеми форматами:
https://docs.microsoft.com/en-us/dotnet/api/system.icloneable?view=netstandard-2.0