Pergunta

Existe uma razão específica para que um ICloneable<T> genérico não existe?

Seria muito mais confortável, se eu não teria necessidade de lançá-lo toda vez que eu clone algo.

Foi útil?

Solução

ICloneable é considerado um mau API agora, uma vez que não especifica se o resultado é um profundo ou uma cópia superficial. Eu acho que é por isso que eles não melhorar esta interface.

Você provavelmente pode fazer um método de extensão clonagem digitado, mas acho que isso exigiria um nome diferente, pois os métodos de extensão têm menos prioridade do que os originais.

Outras dicas

Além de resposta de Andrey (que eu concordo com, +1) - quando ICloneable é feito, você também pode escolher implementação explícita para fazer a Clone() pública devolver um objeto tipado:

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

É claro que há um segundo problema com um ICloneable<T> genérica -. Herança

Se eu tiver:

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

E eu implementado ICloneable<T>, então faço para implementar ICloneable<Foo>? ICloneable<Bar>? Você rapidamente começar a implementar uma série de interfaces idênticos ... Compare com um elenco ... e é realmente tão ruim?

Eu preciso perguntar, o que exatamente você faria com a interface que não implementá-lo? Interfaces são normalmente apenas útil quando você joga para ele (ou seja, faz este apoio classe 'IBar'), ou ter parâmetros ou setters que levá-lo (ou seja, eu tomo um 'IBar'). Com ICloneable - fomos através de todo o quadro e não conseguiu encontrar um único lugar uso que era algo diferente de uma implementação do mesmo. Nós também não conseguiram encontrar qualquer uso no 'mundo real', que também faz algo diferente do que implementá-lo (no ~ 60.000 aplicativos que temos acesso a).

Agora, se você gostaria apenas de fazer cumprir um padrão que você deseja que seus objetos 'Cloneable' de implementar, que é um uso completamente bem - e ir em frente. Você também pode decidir sobre o que exatamente "clonagem" significa para você (ou seja, profunda ou superficial). No entanto, nesse caso, não há nenhuma necessidade para nós (BCL) para defini-lo. Nós só definir abstrações na BCL quando há uma necessidade de instâncias de câmbio digitados como a abstração entre bibliotecas não relacionados.

David Kean (BCL Team)

Eu acho que a pergunta "por que" é desnecessário. Há um monte de interfaces / classes / etc ... que é muito útil, mas não é parte da biblioteca de base .NET Frameworku.

Mas, principalmente, você pode fazê-lo sozinho.

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

É muito fácil para escrever o interagir-se se você precisar dele:

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

Depois de ler recentemente o artigo Por que copiar um objeto é uma coisa terrível de se fazer? , penso que esta questão precisa clafirication adicional. Outras respostas aqui fornecer bons conselhos, mas ainda assim a resposta não está completa - por que não ICloneable<T>

  1. Uso

    Assim, você tem uma classe que implementa-lo. Enquanto, anteriormente, que tinha um método que queria ICloneable, ele agora tem que ser genérico para aceitar ICloneable<T>. Você precisará editá-lo.

    Em seguida, você poderia ter conseguido um método que verifica se um objeto is ICloneable. E agora? Você não pode fazer is ICloneable<> e como você não sabe o tipo de objeto em tempo de compilação do tipo, você não pode fazer o método genérico. Primeiro problema real.

    Então, você precisa ter tanto ICloneable<T> e ICloneable, o ex-aplicação desta decisão. Assim, um implementador seria necessário para implementar os dois métodos - object Clone() e T Clone(). Não, obrigado, já temos bastante divertido com IEnumerable.

    Como já salientado, há também a complexidade da herança. Enquanto covariância pode parecer para resolver este problema, um tipo derivado precisa implementar ICloneable<T> de seu próprio tipo, mas já existe um método com a mesma assinatura (= parâmetros, basicamente) - o Clone() da classe base. Efectuar a sua nova interface método clone explícita é inútil, você vai perder a vantagem que você procurou ao criar ICloneable<T>. Então, adicione a palavra-chave new. Mas não se esqueça que você também precisará substituir o Clone() classe base (a implementação tem de permanecer uniforme para todas as classes derivadas, ou seja, para retornar o mesmo objeto de cada método clone, de modo que o método clone de base tem que ser virtual) ! Mas, infelizmente, você não pode ambos os métodos override e new com a mesma assinatura. Escolhendo a primeira palavra-chave, você perderia o objetivo que você queria ter quando adicionar ICloneable<T>. Chossing o segundo, você quebrar a própria interface, fazendo métodos que devem fazer o mesmo retorno objetos diferentes.

  2. Ponto

    Você quer ICloneable<T> para o conforto, mas o conforto não é o que as interfaces são projetados para, seu significado é (em OOP geral) para unificar o comportamento de objetos (embora em C #, que é limitada a unificar o comportamento exterior, por exemplo, os métodos e as propriedades, não seu funcionamento).

    Se a primeira razão não tenha convencido ainda, você pode objetar que ICloneable<T> também pode trabalhar de forma restritiva, para limitar o tipo retornado do método clone. No entanto, programador desagradável pode implementar ICloneable<T> onde T não é o tipo que é implementá-lo. Assim, para atingir o seu limite, você pode adicionar um agradável restrição ao parâmetro genérico:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Certamente mais restritiva que a um sem where, você ainda não pode restringir o que T é o tipo que está implementando a interface (você pode derivar de ICloneable<T> de tipo diferente que implementa-lo).

    Você vê, mesmo esta finalidade não poderia ser alcançado (o ICloneable original também falha neste, nenhuma interface pode realmente limitar o comportamento da classe de execução).

Como você pode ver, isso prova fazendo a interface genérica é tanto difícil de implementar plenamente e também realmente desnecessário e inútil.

Mas voltando à pergunta, o que você realmente procuram é ter conforto ao clonar um objeto. Há duas maneiras de fazer isso:

Métodos adicionais

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

Esta solução oferece tanto conforto e comportamento pretendido para os usuários, mas também é muito tempo para implementar. Se não quiser ter o método "confortável" retornando o tipo de corrente, é muito mais fácil terapenas public virtual object Clone().

Então vamos ver a solução "final" - o que em C # é realmente buscou dar nos confortar?

Os métodos de extensão!

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

É nomeado Copiar para não colidir com a corrente Clone métodos (compilador prefere próprios métodos declarados do tipo mais queridos de extensão). A restrição class está lá para velocidade (não requer nulo verificar etc.).

Espero que isso esclarece a razão por que não fazer ICloneable<T>. No entanto, recomenda-se não implementar ICloneable em tudo.

Embora a questão é muito antiga (5 anos de escrever estas respostas :) e já foi respondida, mas eu encontrei este artigo responde a pergunta muito bem, check it aqui

EDIT:

Aqui está a citação do artigo que responde à pergunta (certifique-se de ler o artigo completo, que inclui outras coisas interessantes):

Há muitas referências sobre o apontamento Internet para um post de blog 2003 por Brad Abrams - no momento empregado na Microsoft - em que alguns pensamentos sobre ICloneable são discutidos. A entrada do blog podem ser encontrados neste endereço: Implementação ICloneable . Apesar da enganosa título, este blog não chamadas para implementar ICloneable, principalmente por causa da rasa confusão / deep. extremidades artigo em uma linha reta sugestão: Se você precisa de um mecanismo de clonagem, definir seu próprio Clone, ou Copie metodologia, e garantir que você documentar claramente se é um profunda ou superficial cópia. Um padrão adequado é: public <type> Copy();

Um grande problema é que eles não poderiam restringir T ser da mesma classe. Fore exemplo o que iria impedi-lo de fazer isso:

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

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

}

Eles precisam de uma restrição parâmetro como:

interfact IClonable<T> where T : implementing_type

É uma pergunta muito boa ... Você poderia fazer o seu próprio, no entanto:

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

Andrey diz que é considerado um mau API, mas eu não ouvi nada sobre essa interface se tornar obsoleto. E que iria quebrar toneladas de interfaces de ... O método Clone deve executar uma cópia superficial. Se o objeto também fornece cópia profunda, pode ser usado um Clone sobrecarregado (bool de profundidade).

EDIT:. Padrão eu uso para "clonar" um objeto, está passando um protótipo no construtor

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

Isso remove quaisquer potenciais situações de implementação de código redundante. BTW, falando sobre as limitações de ICloneable, não é realmente até o próprio objeto para decidir se um clone superficial ou clone profundo, ou mesmo um clone parte rasa / parcialmente profunda, deve ser realizada? Devemos realmente se importa, contanto que as obras objeto como previsto? Em algumas ocasiões, uma implementação bem Clone poderia muito bem incluir tanto superficial e clonagem de profundidade.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top