[c#] 왜 ICloneable <T>가 없습니까?

제네릭 ICloneable<T>이 존재하지 않는 특별한 이유 가 있습니까?

내가 무언가를 복제 할 때마다 그것을 캐스팅 할 필요가 없다면 훨씬 더 편안 할 것입니다.



답변

ICloneable은 결과가 딥 카피인지 얕은 카피인지 지정하지 않기 때문에 현재 나쁜 API로 간주됩니다. 이것이 그들이이 인터페이스를 개선하지 않는 이유라고 생각합니다.

유형이 지정된 클로닝 확장 방법을 사용할 수는 있지만 확장 방법은 원래 방법보다 우선 순위가 낮으므로 다른 이름이 필요하다고 생각합니다.


답변

안드레이의 답변뿐만 아니라 (나는 동의 +1) – 때 ICloneable 입니다 수행, 당신은 또한 공개 할 수있는 명시 적 구현을 선택할 수 있습니다 Clone()반환 형식화 된 개체를 :

public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}

물론 일반 ICloneable<T>상속 에는 두 번째 문제가 있습니다.

만약 내가 가지고 있다면:

public class Foo {}
public class Bar : Foo {}

그리고 구현 ICloneable<T>한 다음 구현 ICloneable<Foo>합니까? ICloneable<Bar>? 많은 동일한 인터페이스를 빠르게 구현하기 시작합니다 … 캐스트와 비교할 때 … 정말 나쁩니 까?


답변

인터페이스가 아닌 다른 인터페이스로 정확히 무엇 을 하시겠습니까? 인터페이스는 일반적으로 캐스트 (예 :이 클래스가 ‘IBar’를 지원함)하거나이를 사용하는 매개 변수 또는 설정자가있는 경우에만 유용합니다 (즉, ‘IBar’를 사용함). ICloneable을 사용하여 전체 프레임 워크를 살펴보고 구현 이외의 다른 곳에서는 단일 사용을 찾지 못했습니다. 우리는 또한 ‘실제 세계’에서 구현 이외의 작업을 수행하는 데 실패했습니다 (우리가 액세스 할 수있는 ~ 60,000 개의 앱에서).

이제 ‘복제 가능한’객체를 구현하려는 패턴을 적용하고 싶다면 완전히 좋은 사용법입니다. 또한 “복제”가 자신에게 의미하는 바를 정확하게 결정할 수 있습니다 (즉, 깊거나 얕음). 그러나이 경우 우리 (BCL)가이를 정의 할 필요는 없습니다. 우리는 관련이없는 라이브러리간에 추상화로 유형이 지정된 인스턴스를 교환해야하는 경우에만 BCL에서 추상화를 정의합니다.

데이비드 킨 (BCL 팀)


답변

“왜”라는 질문은 불필요하다고 생각합니다. 매우 유용한 인터페이스 / 클래스 / 등이 많이 있지만 .NET Frameworku 기본 라이브러리의 일부는 아닙니다.

그러나 주로 스스로 할 수 있습니다.

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

public abstract class CloneableBase<T> : ICloneable<T> where T : CloneableBase<T> {
    public abstract T Clone();
    object ICloneable.Clone() { return this.Clone(); }
}

public abstract class CloneableExBase<T> : CloneableBase<T> where T : CloneableExBase<T> {
    protected abstract T CreateClone();
    protected abstract void FillClone( T clone );
    public override T Clone() {
        T clone = this.CreateClone();
        if ( object.ReferenceEquals( clone, null ) ) { throw new NullReferenceException( "Clone was not created." ); }
        return clone
    }
}

public abstract class PersonBase<T> : CloneableExBase<T> where T : PersonBase<T> {
    public string Name { get; set; }

    protected override void FillClone( T clone ) {
        clone.Name = this.Name;
    }
}

public sealed class Person : PersonBase<Person> {
    protected override Person CreateClone() { return new Person(); }
}

public abstract class EmployeeBase<T> : PersonBase<T> where T : EmployeeBase<T> {
    public string Department { get; set; }

    protected override void FillClone( T clone ) {
        base.FillClone( clone );
        clone.Department = this.Department;
    }
}

public sealed class Employee : EmployeeBase<Employee> {
    protected override Employee CreateClone() { return new Employee(); }
}


답변

필요한 경우 인터페이스를 직접 작성하는 것이 매우 쉽습니다 .

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


답변

최근에 객체 복사가 끔찍한 이유 기사를 읽은 적이 있습니까? , 나는이 질문에 추가 clafirication이 필요하다고 생각합니다. 여기에있는 다른 답변은 좋은 조언을 제공하지만 여전히 답변이 완전하지 않습니다 ICloneable<T>. 왜 안 됩니까?

  1. 용법

    그래서 그것을 구현하는 클래스가 있습니다. 이전에는 원하는 방법이 ICloneable있었지만 이제는 일반적으로 받아 들여야 ICloneable<T>합니다. 편집해야합니다.

    그런 다음 object 여부를 확인하는 메소드를 가질 수 있습니다 is ICloneable. 지금 무엇? 당신은 할 수 없으며 is ICloneable<>컴파일 유형의 객체 유형을 알지 못하므로 메소드를 일반으로 만들 수는 없습니다. 첫 번째 진짜 문제.

    따라서 전자 ICloneable<T>ICloneable후자를 모두 구현해야합니다. , 따라서 구현은 두 가지 방법을 구현해야합니다 – object Clone()T Clone(). 아니요. 감사 IEnumerable합니다.

    이미 지적했듯이 상속의 복잡성도 있습니다. 공분산이이 문제를 해결하는 것처럼 보이지만 파생 된 형식 ICloneable<T>은 자체 형식 을 구현해야 하지만 Clone()기본 클래스 와 동일한 서명 (기본적으로 매개 변수)을 가진 메서드가 이미 있습니다. 새로운 클론 메소드 인터페이스를 명시 적으로 만드는 것은 의미가 없으며, 생성 할 때 추구했던 이점을 잃게됩니다 ICloneable<T>. new키워드를 추가하십시오 . 그러나 기본 클래스를 재정의해야한다는 것을 잊지 마십시오 ‘ Clone()(구현은 모든 파생 클래스에 대해 균일하게 유지되어야합니다 (즉, 모든 클론 메소드에서 동일한 객체를 반환해야하므로 기본 클론 메소드는이어야 함 virtual))! 그러나, 불행하게도, 당신은 할 수 없습니다 모두 overridenew동일한 서명을 가진 메소드. 첫 번째 키워드를 선택하면를 추가 할 때 원하는 목표를 잃게됩니다 ICloneable<T>. 두 번째 것을 선택하면 인터페이스 자체가 깨져서 동일한 작업을 수행하는 메서드가 다른 객체를 반환하게됩니다.

  2. 포인트

    당신이 원하는 ICloneable<T>편안하지만 편안 C #으로,가, 예를 들어 메서드와 속성을 외부 행동을 통일로 제한되어 있지만, (객체의 동작을하지 단일화 (일반 OOP에서) 그 의미는, 위해 설계 어떤 인터페이스를하지 않습니다 그들의 작업).

    첫 번째 이유가 아직 확실하지 않은 경우, ICloneable<T>복제 메소드에서 리턴 된 유형을 제한하기 위해 제한적으로 작동 할 수있는 오브젝트를 반대 할 수 있습니다. 그러나 불쾌한 프로그래머는 ICloneable<T>T가 그것을 구현하는 유형이 아닌 곳에서 구현할 수 있습니다. 따라서 제한을 달성하려면 일반 매개 변수에 좋은 제약 조건을 추가 할 수 있습니다.
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    확실히없는 매개 변수 보다 더 제한적으로 T 는 인터페이스를 구현하는 유형으로 where제한 할 수 없습니다 ( 다른 유형 에서 파생 될 수 있음 ) 그것을 구현합니다).ICloneable<T>

    당신은이 목적조차도 달성 할 수 없었습니다 (원본 ICloneable도 이것에 실패하고 인터페이스가 실제로 구현 클래스의 동작을 제한 할 수는 없습니다).

보시다시피, 이것은 일반 인터페이스를 완전히 구현하기가 어렵고 실제로 불필요하고 쓸모 없게 만듭니다.

그러나 질문으로 돌아가서, 실제로 추구하는 것은 객체를 복제 할 때 위로를 얻는 것입니다. 이를 수행하는 두 가지 방법이 있습니다.

추가 방법

public class Base : ICloneable
{
    public Base Clone()
    {
        return this.CloneImpl() as Base;
    }

    object ICloneable.Clone()
    {
        return this.CloneImpl();
    }

    protected virtual object CloneImpl()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public new Derived Clone()
    {
        return this.CloneImpl() as Derived;
    }

    protected override object CloneImpl()
    {
        return new Derived();
    }
}

이 솔루션은 사용자에게 편안함과 의도 된 동작을 모두 제공하지만 구현하기에는 너무 길다. 현재 유형을 리턴하는 “편안한”메소드를 원하지 않으면 그냥 사용하는 것이 훨씬 쉽다 public virtual object Clone().

그렇다면 “궁극적 인”솔루션을 보도록하겠습니다. C #에서 실제로 우리에게 편안함을주는 것은 무엇입니까?

확장 방법!

public class Base : ICloneable
{
    public virtual object Clone()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public override object Clone()
    {
        return new Derived();
    }
}

public static T Copy<T>(this T obj) where T : class, ICloneable
{
    return obj.Clone() as T;
}

현재 Clone 메서드 와 충돌하지 않도록 Copy 라는 이름이 지정됩니다 (컴파일러는 확장 메서드보다 형식 자체 선언 된 메서드를 선호합니다). 제약은 속도가 (등을 확인 NULL 필요하지 않습니다)입니다.class

나는 이것이 왜하지 않는 이유를 명확히하기를 바랍니다 ICloneable<T>. 그러나 전혀 구현하지 않는 것이 좋습니다 ICloneable.


답변

질문이 매우 오래되었지만 (이 답변을 작성한 후 5 년 🙂 이미 답변되었지만이 기사가 질문에 잘 대답한다는 것을 알았습니다. 여기 에서 확인 하십시오.

편집하다:

다음은 질문에 대답하는 기사의 인용문입니다 (전체 기사를 읽으십시오. 다른 흥미로운 것들이 포함되어 있습니다).

인터넷상에서 2003 년 브래드 아브람 (Brad Abrams)의 블로그 게시물을 가리키고있다. 블로그 항목은 다음 주소에서 찾을 수 있습니다 . ICloneable 구현 . 오해의 소지가 있지만이 블로그 항목은 주로 얕은 / 깊은 혼동 때문에 ICloneable을 구현하지 않아야합니다. 복제 제안이 필요한 경우 복제 기법 또는 복제 방법을 정의하고 깊거나 얕은 사본인지 명확하게 문서화해야합니다. 적절한 패턴은 다음과 같습니다.public <type> Copy();