Я расширяю существующий интерфейс IClonable
, используя ковариантный общий аргумент.StackOverflowException с использованием реализации эксклюзионного интерфейса с ковариантным общим параметром
public interface ICloneable<out T> : ICloneable
{
new T Clone();
}
Теперь я реализовал этот интерфейс в базовом классе.
public class Base : ICloneable<Base>
{
public string StrValue { get; set; }
Base ICloneable<Base>.Clone()
{
var result = (Base)FormatterServices.GetUninitializedObject(this.GetType());
result.StrValue = this.StrValue;
return result;
}
public virtual object Clone()
{
return ((ICloneable<Base>)this).Clone();
}
}
Вызов Clone()
работает, как ожидалось, и возвращает новый экземпляр базового класса, имеющего те же значения.
Я создал производный класс из Base
, который реализует интерфейс ICloneable<T>
снова вернуть этот новый тип:
public class Sub : Base, ICloneable<Sub>
{
public int IntValue { get; set; }
Sub ICloneable<Sub>.Clone()
{
var result = (Sub)base.Clone();
result.IntValue = this.IntValue;
return result;
}
public override object Clone()
{
return ((ICloneable<Sub>)this).Clone();
}
}
Но если я позвоню Clone()
на примере Sub
я бегу в StackOverflowException потому object Base.Clone()
звонки Sub ICloneable<Sub>.Clone()
из класс Sub
.
Проблема заключается в ковариантном параметре общего типа. Если я удалю out
, все будет работать должным образом.
Вопрос в том, почему ((ICloneable<Base>)this).Clone()
указывает на Sub.Clone()
?
Сочетанный и контравариантный синтаксический сахар, и компилятор ищет наименьший возможный тип в иерархии деривации? Это означает, что ICloneable<Base>
будет изменен на ICloneable<Sub>
компилятором.
Я не нашел официального объяснения, объясняющего это поведение.
Testcode (исключая интерфейс и Base
и Sub
):
var b = new Sub { StrValue = "Hello World.", IntValue = 42 };
var b2 = (Base)b.Clone();
_ CLR будет искать время выполнения для любой переопределенной реализации 'Clone'_ - возможно, я не понял вас правильно, но нет переопределенной реализации' ICloneable .Clone() '.Единственное переопределение «Clone» - это нелогичный «IClonable.Clone()». Возможно, вы имеете в виду, что CLR ищет более специфический тип во время выполнения, находит его и меняет вызов на более конкретный тип. Это нормально. Есть ли способ предотвратить выполнение CLR в этом конкретном случае? –
@ Vera rind Поскольку 'ICloneable' является ковариантным, когда вы вызываете '((ICloneable ) this) .Clone();', литье 'this' соответствует' ICloneable ', а не 'ICloneable '. –
Да, я понимаю. но последний вопрос все еще открыт: есть ли способ предотвратить выполнение CLR в этом конкретном случае? –