programing

복제할 수 없는 이유는 무엇입니까?

testmans 2023. 5. 5. 08:44
반응형

복제할 수 없는 이유는 무엇입니까?

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

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

에도 (하는 + - Andrey가 (가의동제 +1) 외에답 - 언제 때ICloneable 완료되었습니다. 또한 공개하기 위해 명시적인 구현을 선택할 수 있습니다.Clone()입력한 개체 반환:

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

▁a▁▁issue▁second다 두 번째 문제가 있습니다.ICloneable<T>상속 재산

내가 가지고 있는 경우:

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

그리고 나는 구현했습니다.ICloneable<T>그럼 제가 구현할까요?ICloneable<Foo>?ICloneable<Bar>많은 동일한 인터페이스를 빠르게 구현하기 시작합니다.?그리고 정말 그렇게 나쁜가요?

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

당신은 아마 형식적인 복제 확장 방법을 할 수 있지만, 확장 방법의 우선 순위가 원래보다 낮기 때문에 다른 이름이 필요할 것 같습니다.

저는 당신이 인터페이스를 구현하는 것 외에 정확히 무엇을 할 것인지 묻고 싶습니다.인터페이스는 일반적으로 캐스트할 때만 유용합니다(즉, 이 클래스가 지원합니까).'IBar') 또는 이를 사용하는 매개 변수 또는 설정자가 있습니다(즉, 'I'를 사용함).IC 복제가 가능한 상태에서 전체 프레임워크를 검토한 결과 구현 이외의 용도를 찾을 수 없었습니다.또한 '실제 환경'에서 이를 구현하는 것 외에 다른 기능을 하는 용도(접속 가능한 최대 60,000개 앱)도 찾을 수 없었습니다.

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

David Kean (BCL 팀)

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

하지만 주로 당신이 직접 할 수 있습니다.

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 (clone is null ) {
            throw new NullReferenceException( "Clone was not created." );
        }

        this.FillClone(clone);
        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();
}

최근에 "왜 물체를 복사하는 것이 끔찍한 일인가?"라는 기사를 읽었습니다.이 질문은 추가적인 확증이 필요하다고 생각합니다.여기에 있는 다른 답변은 좋은 조언을 제공하지만, 여전히 답변이 완전하지 않습니다. - 왜 안 되죠?ICloneable<T>?

  1. 사용.

    이를 구현하는 클래스가 있습니다.이전에 당신이 원하는 방법이 있었을 것입니다.ICloneable이제 그것은 받아들여지기 위해 일반적이어야 합니다.ICloneable<T>편집해야 합니다.

    그러면, 당신은 물체가 어떤 물체인지 확인할 수 있는 방법을 얻을 수 있었을 것입니다.is ICloneable?그럼 어쩌라는 거야?은 할 수 요.is ICloneable<>그리고 컴파일 형식에서 객체의 유형을 모르기 때문에 메소드를 제네릭으로 만들 수 없습니다.첫번째 진짜 문제.

    그래서 당신은 두 가지를 모두 가져야 합니다.ICloneable<T>그리고.ICloneable후자를 구현하는 전자따라서 구현자는 두 가지 방법을 모두 구현해야 합니다.object Clone()그리고.T Clone(). 아요니, 니다합으로 즐깁니다. 우리는 이미 충분히 즐겼습니다.IEnumerable.

    이미 지적했듯이 상속의 복잡성도 있습니다.공분산이 이 문제를 해결하는 것처럼 보일 수 있지만 파생된 유형은 다음을 구현해야 합니다.ICloneable<T>자체 유형이지만 동일한 서명(= 매개 변수, 기본적으로)을 가진 방법이 이미 있습니다. - 더Clone()새로운 으로 만드는 의미가 없으며, 생성할 때 잃게 .ICloneable<T>그래서 추가합니다.new클래스의 기본 클래스을 잊지 마세요.Clone()모든 파생 클래스에 기본 는 (즉, 모클메에서동일객한드반체대모에는는해메구드환현소이하균)이어야 합니다.virtual은 둘 다 할 수 .override그리고.new동일한 서명을 가진 메서드입니다.첫 번째 키워드를 선택하면 추가할 때 원하는 목표를 잃게 됩니다.ICloneable<T>두 번째를 선택하면 인터페이스 자체가 깨지고, 동일한 작업을 수행해야 하는 방법이 서로 다른 개체를 반환합니다.

  2. 포인트

    너는 원한다ICloneable<T>편안함을 위해, 그러나 편안함은 인터페이스가 설계된 것이 아니며, 그들의 의미는 (일반적으로 OOP) 객체의 행동을 통합하는 것입니다(그러나 C#에서는 외부 행동, 예를 들어 방법과 특성을 통합하는 것으로 제한됩니다).

    만약 첫 번째 이유가 아직 당신을 납득시키지 못했다면, 당신은 반대할 수 있습니다.ICloneable<T>복제 방법에서 반환되는 형식을 제한하기 위해 제한적으로 작동할 수도 있습니다.하만지, 형는프래는머를 할 수 .ICloneable<T>여기서 T는 이를 구현하는 유형이 아닙니다.따라서 제한을 달성하기 위해 일반 매개 변수에 적절한 제약 조건을 추가할 수 있습니다.
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    실히없는것더보제다니입한적다확▁without▁one다가 없는 보다 더 입니다.where당신은 여전히 T가 인터페이스를 구현하는 유형이라는 을 제한할 수 없습니다.ICloneable<T>이를 구현하는 다양한 유형).

    수 의 알시피달, 이목도원수성다없니할습었차래조적다원(▁youthe수▁couldn다없니▁original습었▁this▁purpose달,'래▁(▁even▁achieved성▁be)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;
}

현재 복제 메소드와 충돌하지 않도록 복사라는 이름입니다(컴파일러는 확장 메소드보다 형식 자체의 선언 메소드를 선호합니다).class속도에 대한 제약이 있습니다(Null 확인 등이 필요하지 않음).

나는 이것이 왜 만들지 않는 이유를 명확히 하기를 바랍니다.ICloneable<T>그러나 다음을 구현하지 않는 것이 좋습니다.ICloneable조금도.

큰 문제는 그들이 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

아주 좋은 질문입니다...하지만 당신은 당신만의 것을 만들 수 있습니다.

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

Andrey는 그것이 나쁜 API로 간주된다고 말하지만, 나는 이 인터페이스가 더 이상 사용되지 않는다는 것을 듣지 못했습니다.그렇게 되면 수많은 인터페이스가 깨지고...복제 방법은 얕은 복사를 수행해야 합니다.개체가 전체 복사본도 제공하는 경우 오버로드된 클론(bool deep)을 사용할 수 있습니다.

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

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

이렇게 하면 잠재적인 중복 코드 구현 상황이 제거됩니다.BTW, ICloneable의 한계에 대해 이야기하자면, 얕은 클론 또는 깊은 클론 또는 부분적으로 얕은/부분적으로 깊은 클론을 수행할지 여부는 정말로 개체 자체에 달려 있지 않습니까?물건이 의도한 대로 작동하는 한 우리가 정말 신경을 써야 할까요?경우에 따라 양호한 클론 구현에는 얕은 클로닝과 깊은 클로닝이 모두 포함될 수 있습니다.

언급URL : https://stackoverflow.com/questions/536349/why-no-icloneablet

반응형