왜 ICloneable <T>가 없습니까?


222

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

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


6
아니! '이유'와 관련하여 모든 것을 존중하면서, 나는 당신에게 동의합니다.
Shimmy Weitzhandler

Microsoft가 정의한 것은 좋은 일이었습니다. 직접 만든 인터페이스의 문제는 두 개의 서로 다른 어셈블리의 인터페이스가 의미 상 동일하더라도 호환되지 않는다는 것입니다. 인터페이스를 디자인 할 때 Clone, Self 및 CloneIfMutable의 세 멤버가 있으며 모두 T를 리턴합니다 (마지막 멤버는 Clone 또는 Self를 적절하게 리턴 함). Self 멤버는 ICloneable (of Foo)을 매개 변수로 수락 한 다음 형식 캐스팅없이 Foo로 사용할 수 있습니다.
supercat

이를 통해 상속 가능한 클래스가 보호 된 "복제"방법을 공개하고 공개 된 파생 상품을 공개 한 파생 클래스를 갖는 적절한 복제 클래스 계층 구조를 사용할 수 있습니다. 예를 들어, Pile, CloneablePile : Pile, EnhancedPile : Pile 및 CloneableEnhancedPile : EnhancedPile을 가질 수 있으며, 복제 된 경우 (공개 복제 방법을 모두 공개하지는 않지만), MoreEnhancedPile : EnhancedPile (손상 될 수 있음) 복제되었지만 복제 방법을 공개하지 않는 경우). ICloneable (of Pile)을 허용하는 루틴은 CloneablePile 또는 CloneableEnhancedPile을 허용 할 수 있습니다.
supercat

... CloneableEnhancedPile이 CloneablePile에서 상속받지 않더라도. EnhancedPile이 CloneablePile에서 상속 된 경우, MoreEnhancedPile은 공개 복제 방법을 노출해야하며이를 복제 할 코드로 전달하여 Liskov Substitutability Principle을 위반할 수 있습니다. CloneableEnhancedPile은 ICloneable (Of EnhancedPile)을 구현하고 ICloneable (Of Pile)을 암시함으로써 복제 가능 파일 Pile을 기대하는 루틴으로 전달 될 수 있습니다.
supercat

답변:


111

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

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


8
동의하지 않거나 나쁘거나 나쁘지 않습니다. SHALLOW 클로닝에 언젠가는 매우 유용하며,이 경우 불필요한 권투 및 언 박싱을 저장하기 위해 Clone <T>이 필요합니다.
Shimmy Weitzhandler

2
@AndreyShchekin : 깊은 복제와 얕은 복제에 대해 분명하지 않은 것은 무엇입니까? List<T>복제 방법이있는 경우 List<T>원래 목록의 ID와 동일한 ID를 가진 항목 을 생성 할 것으로 예상되지만 하나의 목록에 수행 된 작업이 영향을 미치지 않도록 필요한 경우 내부 데이터 구조가 복제 될 것으로 예상합니다 다른쪽에 저장된 품목 의 신원 . 모호성은 어디에 있습니까? 복제와 관련하여 더 큰 문제는 "다이아몬드 문제"의 변형과 함께 발생합니다. CloneableFoo[공개적으로 복제 FooCloneableDerivedFoo수 없음 ]에서 상속 된 경우 다음 에서 파생 되어야합니다 .
supercat

1
@ supercat : identity예를 들어 (목록 목록의 경우) 목록 자체 를 무엇으로 고려할 것이므로 기대를 완전히 이해한다고 말할 수는 없습니다 . 그러나이를 무시하면 전화 나 구현시 사람들이 기대할 수있는 유일한 아이디어는 아닙니다 Clone. 다른 목록을 구현하는 도서관 저자가 귀하의 기대를 따르지 않으면 어떻게됩니까? API는 분명하고 모호하지 않아야합니다.
Andrey Shchekin 1

2
@supercat 그래서 당신은 얕은 복사에 대해 이야기하고 있습니다. 그러나 딥 카피에는 근본적으로 아무런 문제가 없습니다. 예를 들어 메모리 내 객체에서 가역 트랜잭션을 수행하려는 경우입니다. 귀하의 기대는 귀하의 사용 사례를 기반으로하며 다른 사람들은 다른 기대를 가질 수 있습니다. 개체를 개체에 넣거나 그 반대의 경우 결과가 예기치 않게 나타납니다.
Andrey Shchekin

5
@ supercat 당신은 응용 프로그램의 일부가 아니라 .NET 프레임 워크의 일부임을 고려하지 않습니다. 따라서 딥 클로닝을하지 않는 클래스를 만들면 다른 사람이 딥 클로닝을 수행하고 Clone모든 파트를 호출하는 클래스를 만들면 해당 파트가 본인 또는 그 사람에 의해 구현되었는지 여부에 따라 예상대로 작동하지 않습니다. 깊은 복제를 좋아하는 사람. 패턴에 대한 요점은 유효하지만 API에 IMHO가 있다는 것은 명확하지 않습니다 ShallowCopy. 요점을 강조하기 위해 호출 하거나 전혀 제공하지 않아야합니다.
Andrey Shchekin

141

안드레이의 답변뿐만 아니라 (나는 동의 +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>? 많은 동일한 인터페이스를 빠르게 구현하기 시작합니다 ... 캐스트와 비교할 때 ... 정말 나쁩니 까?


10
+1 나는 항상 공분산 문제가 진짜 이유라고 생각했습니다
Tamas Czinege

이것에 문제가 있습니다 : 명시 적 인터페이스 구현은 private 이어야합니다.이 시점에서 Foo를 ICloneable로 캐스팅해야 할 경우 문제가 발생합니다 ... 여기에 뭔가 빠졌습니까?
Joel in Gö

3
내 실수 : 개인으로 정의되었지만 Foo를 IClonable (C # 2nd Ed, p.319를 통해 CLR)로 캐스팅 하면 메소드 호출 됩니다 . 이상한 디자인 결정처럼 보이지만 거기에 있습니다. 따라서 Foo.Clone ()은 첫 번째 방법을 제공하고 ((ICloneable) Foo) .Clone ()은 두 번째 방법을 제공합니다.
Joel in Gö

실제로 명시 적 인터페이스 구현은 엄밀히 말하면 공개 API의 일부입니다. 그것들은 캐스팅을 통해 공개적으로 호출 가능합니다.
Marc Gravell

8
제안 된 솔루션은 처음에는 훌륭해 보입니다 ... 클래스 계층 구조에서 'public Foo Clone ()'이 파생 클래스에서 가상으로 재정의되어 자체 유형을 반환하고 (자체 필드를 복제 할 수 있음) 강좌). 안타깝게도 재정의 (반환 된 공분산 문제)에서 반환 유형을 변경할 수 없습니다. 따라서 원래 문제로 다시 돌아옵니다. 명시적인 구현은 실제로 많은 것을 사지 않습니다 ( 'object'대신 계층의 기본 유형을 반환하는 것 제외). Clone () 호출 결과. 왝!
Ken Beckett

18

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

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

데이비드 킨 (BCL 팀)


1
제네릭이 아닌 ICloneable은 그다지 유용하지 않지만 , 단일 유형의 메소드 를 사용하여 ICloneable<out T>상속 된 경우 매우 유용 할 수 있습니다 . 하나는 종종 "복제 가능한 것"을 필요로하지 않지만, 복제 가능한 것을 필요로 할 수도 있습니다 . 복제 가능한 객체가 구현 하는 경우 복제 가능한 파생물이 모두 공통 조상 을 공유 하지는 않지만 복제 가능한 객체 를 필요로하는 루틴 은 type의 매개 변수를 허용 할 수 있습니다 . ISelf<out T>SelfTTISelf<itsOwnType>TICloneable<T>T
supercat

감사. 위에서 언급 한 캐스트 상황과 비슷합니다 (예 : '이 클래스는'IBar '를 지원합니다). 안타깝게도 우리는 T가 복제 가능하다는 사실을 실제로 활용하는 매우 제한적이고 고립 된 시나리오 만 제시했습니다. 상황을 염두에두고 있습니까?
David Kean

.net에서 때때로 어색한 것 중 하나는 컬렉션의 내용을 가치로 볼 때 가변 컬렉션을 관리하는 것입니다 (즉, 컬렉션에있는 항목 세트를 나중에 알고 싶습니다). ICloneable<T>병렬 변경 가능 및 불변 클래스를 유지하기위한 더 넓은 프레임 워크가 더 도움이 될 수 있지만 이와 같은 것이 유용 할 수 있습니다. 즉, 필요 코드의 몇 가지 유형이 무엇인지 볼 Foo이 들어 있지만, 어느 쪽도 그것을 돌연변이도 이제까지 변화는 사용할 수 없습니다 것으로 예상 것 없다 IReadableFoo..., 동안
supercat

...의 내용을 유지하려는 코드 Foo는 사용할 수있는 ImmutableFoowhile 코드를 사용할 수 있습니다 MutableFoo. 모든 유형의 코드 IReadableFoo는 변경 가능하거나 변경 불가능한 버전을 얻을 수 있어야합니다. 이러한 프레임 워크는 훌륭하지만 불행히도 일반적인 방식으로 설정하는 좋은 방법을 찾을 수 없습니다. 클래스에 대해 읽기 전용 래퍼를 만드는 일관된 방법이 있다면, ' ICloneable<T>를 보유한 클래스의 불변 사본을 만드는 데 이러한 것을 사용할 수 있습니다 T.
supercat

@supercat를 복제하려는 경우 List<T>, 복제 된 List<T>원본이 원본 컬렉션의 모든 동일한 객체에 대한 포인터를 보유하는 새로운 컬렉션이 되도록하려면 ICloneable<T>. 첫 번째는 것입니다 Enumerable.ToList()확장 방법 : List<foo> clone = original.ToList();두 번째는이다 List<T>소요 생성자 IEnumerable<T>: List<foo> clone = new List<foo>(original);내가 확장 방법은 아마 생성자를 호출 생각하지만, 이들 모두는 당신이 요청하는 일을 할 것입니다. ;)
CptRobby

12

"왜"라는 질문은 불필요하다고 생각합니다. 매우 유용한 인터페이스 / 클래스 / 등이 많이 있지만 .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(); }
}

상속 가능한 클래스에 대해 실행 가능한 복제 방법은 Object.MemberwiseClone을 시작점으로 사용해야합니다 (그렇지 않으면 리플렉션 사용). 그렇지 않으면 복제본이 원본 객체와 동일한 유형일 것이라는 보장이 없기 때문입니다. 관심이 있다면 아주 좋은 패턴이 있습니다.
supercat

@ supercat 왜 대답하지 않습니까?
nawfal

"매우 유용하지만 .NET Frameworku 기본 라이브러리의 일부가 아닌 많은 인터페이스 / 클래스 / 등이 있습니다." 예를 들어 주시겠습니까?
Krythic

1
@Krythic 즉 : b-tree, red-black tree, circle buffer와 같은 고급 데이터 구조. '09 년에는 튜플, 일반적인 약한 참조, 동시 수집이
없었습니다

10

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

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

나는 저작권의 명예로 인해 링크가 깨 졌기 때문에 발췌 문장을 포함시켰다.
Shimmy Weitzhandler

2
그리고 그 인터페이스는 정확히 어떻게 작동합니까? 복제 작업의 결과를 T 유형으로 캐스트 할 수 있으려면 복제되는 개체가 T에서 파생되어야합니다. 이러한 유형 제약 조건을 설정할 방법이 없습니다. 사실, iCloneable 반환 결과를 볼 수있는 유일한 것은 iCloneable 유형입니다.
supercat

@supercat : 그렇습니다. Clone ()이 정확히 반환하는 것입니다 : ICloneable <T>를 구현하는 T. MbUnit은 수년간 이 인터페이스를 사용해 왔 으므로 작동합니다. 구현에 관해서는 MbUnit의 소스를 들여다보십시오.
Mauricio Scheffer

2
@Mauricio Scheffer : 무슨 일인지 알겠습니다. 실제로 iCloneable (of T)을 구현하는 유형은 없습니다. 대신 각 유형은 iCloneable을 구현합니다. 그것은 효과가있을 것입니다.하지만 그것이하는 일은 모두 캐스트를 옮기는 것입니다. 상속 제약 조건과 인터페이스 제약 조건이있는 것으로 필드를 선언 할 수있는 방법이 없습니다. 복제 방법이 반복 제 가능 (보호 된 방법으로 만 노출 된 복제) 기본 유형의 객체를 반환하지만 복제 가능한 유형 제약 조건이있는 객체를 반환하는 것이 도움이됩니다. 이는 객체가 염기 유형의 반-복제 가능한 유도체의 복제 가능한 유도체를 허용 할 수있게한다.
supercat

@Mauricio Scheffer : BTW, ICloneable (of T)도 읽기 전용 Self 속성 (return type T)을 지원할 것을 제안합니다. 이것은 메소드가 ICloneable (of Foo)을 받아들이고 (Foot로 자체 속성을 통해) 사용할 수있게합니다.
supercat

9

최근에 객체 복사가 끔찍한 이유 기사를 읽은 적이 있습니까? , 나는이 질문에 추가 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.


부록 # 1 : 제네릭의 유일한 사용 ICloneable방법은 Clone 메서드의 복싱을 우회 할 수있는 값 유형에 대해서만 사용되며 , 값을 unboxed로 나타냅니다. 또한 구조를 자동으로 (얕게) 복제 할 수 있으므로 구현할 필요가 없습니다 (딥 카피를 구체적으로 지정하지 않는 한).
IllidanS4는

2

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

편집하다:

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

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


1

큰 문제는 T를 동일한 클래스로 제한 할 수 없다는 것입니다. 예를 들어, 이렇게하지 못하는 이유는 다음과 같습니다.

interface IClonable<T>
{
    T Clone();
}

class Dog : IClonable<JackRabbit>
{
    //not what you would expect, but possible
    JackRabbit Clone()
    {
        return new JackRabbit();
    }

}

다음과 같은 매개 변수 제한이 필요합니다.

interfact IClonable<T> where T : implementing_type

8
그것은 나쁘지 않은 것으로 보인다 class A : ICloneable { public object Clone() { return 1; } /* I can return whatever I want */ }
Nikola Novak

문제가 무엇인지 실제로 알지 못합니다. 어떤 인터페이스도 구현이 합리적으로 작동하도록 할 수는 없습니다. 경우에도 ICloneable<T>제한 할 수있는 T자신의 유형과 일치하는, 그의 구현을 강요하지 것이다 Clone()원격으로는 복제 된시 객체를 닮은 반환 아무것도. 또한 인터페이스 공분산을 사용하는 경우 구현 ICloneable된 클래스를 봉인하고 인터페이스에 자체 반환되는 속성이 ICloneable<out T>포함 되도록 하는 것이 가장 좋습니다 Self.
supercat

... 캐스팅 또는 제한 소비자가 ICloneable<BaseType>또는 ICloneable<ICloneable<BaseType>>. BaseType문제는이 있어야 protected하는 구현 형태에 의해 호출 될 것이다 복제에 대한 방법을 ICloneable. 이 디자인은 Container, a CloneableContainer, a FancyContainer및 a 를 가질 수 있으며 , CloneableFancyContainer후자는 복제 가능한 파생물 Container을 필요로하거나 FancyContainer(복제 가능한지 신경 쓰지 않는) 코드에서 사용할 수 있습니다.
supercat

이러한 디자인을 선호하는 이유는 유형을 눈에 띄게 복제 할 수 있는지 여부는 다른 측면과 직교하기 때문입니다. 예를 들어, FancyList현명하게 복제 할 수 있는 유형이있을 수 있지만 파생물은 디스크 파일 (생성자에 지정된)에 자동으로 상태를 유지할 수 있습니다. 파생 된 형식은 상태를 변경 가능한 단일 톤 (파일)의 상태에 연결하기 때문에 복제 할 수 없었지만 대부분의 기능이 필요 FancyList하지만 필요하지 않은 곳에서는 파생 된 형식의 사용을 배제해서는 안됩니다 그것을 복제합니다.
supercat

+1-이 답변은 제가 본 것 중 실제로 유일하게 질문에 답변 한 답변입니다.
IllidanS4는

0

아주 좋은 질문입니다 ...하지만 스스로 만들 수도 있습니다.

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

Andrey는 나쁜 API로 간주되지만이 인터페이스에 대한 지원이 중단되는 소식을 듣지 못했습니다. 그리고 그것은 수많은 인터페이스를 망칠 것입니다 ... Clone 메소드는 얕은 복사를 수행해야합니다. 객체가 딥 카피도 제공하는 경우 오버로드 된 클론 (bool deep)을 사용할 수 있습니다.

편집 : 개체를 "복제"하는 데 사용하는 패턴은 생성자에서 프로토 타입을 전달합니다.

class C
{
  public C ( C prototype )
  {
    ...
  }
}

이것은 잠재적 인 중복 코드 구현 상황을 제거합니다. ICloneable의 한계에 대해 이야기하는 BTW는 얕은 클론 또는 딥 클론, 또는 심지어 얕은 / 부분 딥 클론을 수행해야하는지 결정하는 것이 실제로 객체 자체에 달려 있지 않습니까? 대상이 의도 한대로 작동하는 한 우리가 정말로 관심을 가져야합니까? 경우에 따라 우수한 복제 구현에는 얕은 복제와 깊은 복제가 모두 포함될 수 있습니다.


"나쁜"은 더 이상 사용되지 않음을 의미하지 않습니다. 그것은 단순히 "사용하지 않는 편이 나을 것"을 의미합니다. "나중에 ICloneable 인터페이스를 제거 할 것"이라는 의미에서 더 이상 사용되지 않습니다. 그들은 당신이 그것을 사용하지 말 것을 권장하고, 제네릭이나 다른 새로운 기능으로 업데이트하지 않을 것입니다.
jalf

Dennis : Marc의 답변을 참조하십시오. 공분산 / 상속 문제로 인해이 인터페이스는 최소한 약간 복잡한 시나리오에서는 거의 사용할 수 없습니다.
Tamas Czinege

그래, 나는 ICloneable의 한계를 볼 수있다. 물론 클로닝을 사용할 필요는 거의 없다.
baretta
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.