programing

왜 ICloneable이 없는가

new-time 2020. 5. 5. 21:32
반응형

왜 ICloneable이 없는가?


제네릭

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();
}

Having read recently the article Why Copying an Object is a terrible thing to do?, I think this question needs additional clafirication. Other answers here provide good advices, but still the answer isn't complete - why no ICloneable<T>?

  1. Usage

    So, you have a class that implements it. While previously you had a method that wanted ICloneable, it now has to be generic to accept ICloneable<T>. You would need to edit it.

    Then, you could have got a method that checks if an object is ICloneable. What now? You can't do is ICloneable<> and as you don't know the type of the object at compile-type, you can't make the method generic. First real problem.

    So you need to have both ICloneable<T> and ICloneable, the former implementing the latter. Thus an implementer would need to implement both methods - object Clone() and T Clone(). No, thanks, we already have enough fun with IEnumerable.

    As already pointed out, there is also the complexity of inheritance. While covariance may seem to solve this problem, a derived type needs to implement ICloneable<T> of its own type, but there is already a method with the same signature (= parameters, basically) - the Clone() of the base class. Making your new clone method interface explicit is pointless, you will lose the advantage you sought when creating ICloneable<T>. So add the new keyword. But don't forget that you would also need to override the base class' Clone() (the implementation has to remain uniform for all derived classes, i.e. to return the same object from every clone method, so the base clone method has to be virtual)! But, unfortunately, you can't both override and new methods with the same signature. Choosing the first keyword, you'd lose the goal you wanted to have when adding ICloneable<T>. Chossing the second one, you'd break the interface itself, making methods that should do the same return different objects.

  2. Point

    You want ICloneable<T> for comfort, but comfort is not what interfaces are designed for, their meaning is (in general OOP) to unify the behavior of objects (although in C#, it is limited to unifying the outer behavior, e.g. the methods and properties, not their workings).

    If the first reason hasn't convinced you yet, you could object that ICloneable<T> could also work restrictively, to limit the type returned from the clone method. However, nasty programmer can implement ICloneable<T> where T is not the type that is implementing it. So, to achieve your restriction, you can add a nice constraint to the generic parameter:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Certainly more restrictive that the one without where, you still can't restrict that T is the type that is implementing the interface (you can derive from ICloneable<T> of different type that implements it).

    You see, even this purpose couldn't be achieved (the original ICloneable also fails at this, no interface can truly limit the behavior of the implementing class).

As you can see, this proves making the generic interface is both hard to fully implement and also really unneeded and useless.

But back to the question, what you really seek is to have comfort when cloning an object. There are two ways to do it:

Additional methods

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();
    }
}

This solution provides both comfort and intended behavior to users, but it's also too long to implement. If we didn't want to have the "comfortable" method returning the current type, it is much more easy to have just public virtual object Clone().

So let's see the "ultimate" solution - what in C# is really intented to give us comfort?

Extension methods!

 

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;
}

It's named Copy not to collide with the current Clone methods (compiler prefers the type's own declared methods over extension ones). The class constraint is there for speed (doesn't require null check etc.).

I hope this clarifies the reason why not to make ICloneable<T>. However, it is recommended not to implement ICloneable at all.


Although the question is very old (5 years from writing this answers :) and was already answered, but I found this article answers the question quite well, check it here

EDIT:

Here is the quote from the article that answers the question (make sure to read the full article, it includes other interesting things):

There are many references on the Internet pointing to a 2003 blog post by Brad Abrams - at the time employed at Microsoft - in which some thoughts about ICloneable are discussed. The blog entry can be found at this address: Implementing ICloneable. Despite the misleading title, this blog entry calls not to implement ICloneable, mainly because of shallow/deep confusion. Article ends in a straight suggestion: If you need a cloning mechanism, define your own Clone, or Copy methodology, and ensure that you document clearly whether it is a deep or shallow copy. An appropriate pattern is: public <type> Copy();


A big problem is that they could not restrict T to be the same class. Fore example what would prevent you from doing this:

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

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

}

They need a parameter restriction like:

interfact IClonable<T> where T : implementing_type

It's a very good question... You could make your own, though:

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

Andrey says it's considered a bad API, but i have not heard anything about this interface becoming deprecated. And that would break tons of interfaces... The Clone method should perform a shallow copy. If the object also provides deep copy, an overloaded Clone ( bool deep ) can be used.

EDIT: Pattern i use for "cloning" an object, is passing a prototype in the constructor.

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

This removes any potential redundant code implementation situations. BTW, talking about the limitations of ICloneable, isn't it really up to the object itself to decide whether a shallow clone or deep clone, or even a partly shallow/partly deep clone, should be performed? Should we really care, as long as the object works as intended? In some occasions, a good Clone implementation might very well include both shallow and deep cloning.

참고URL : https://stackoverflow.com/questions/536349/why-no-icloneablet

반응형