Domanda

C'è un motivo particolare per cui un ICloneable<T> generica non esiste?

Sarebbe molto più comodo, se io non avrei bisogno di lanciarlo ogni volta che clonare qualcosa.

È stato utile?

Soluzione

ICloneable è considerato un cattivo API ora, dal momento che non specifica se il risultato è un profondo o una copia. Credo che questo sia il motivo per cui non migliorano questa interfaccia.

Probabilmente si può fare un metodo di estensione clonazione digitato, ma penso che richiederebbe un nome diverso da quando i metodi di estensione hanno meno priorità rispetto a quelli originali.

Altri suggerimenti

In aggiunta alla risposta di Andrey (che sono d'accordo con, +1) - quando ICloneable è fatto, si può anche scegliere esplicito implementazione per rendere il Clone() pubblico restituisce un oggetto tipizzato:

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

Naturalmente c'è un secondo problema con un ICloneable<T> generica -. Eredità

Se ho:

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

E ho implementato ICloneable<T>, quindi faccio a implementare ICloneable<Foo>? ICloneable<Bar>? Si avvia rapidamente l'attuazione di un sacco di interfacce identiche ... Confronta con un cast ... ed è davvero così male?

ho bisogno di chiedere, che cosa esattamente vorresti fare con l'interfaccia diverso implementarlo? Le interfacce sono in genere utili solo quando lanci ad esso (cioè fa questo supporto di classe 'IBar'), o di avere parametri o setter che prendono (vale a dire i assumere un 'IBar'). Con ICloneable - siamo andati attraverso l'intero quadro e non è riuscito a trovare un solo utilizzo da nessuna parte che era qualcosa di diverso da un'implementazione di essa. Abbiamo anche riusciti a trovare qualsiasi uso nel 'mondo reale' che fa anche qualcosa di diverso da attuarlo (in ~ 60.000 applicazioni che abbiamo accesso a).

Ora, se si vorrei solo far rispettare un modello che si desidera che gli oggetti 'clonabile' di implementare, questo è un uso completamente bene - e andare avanti. Si può anche decidere su esattamente che cosa "clonazione" significa per voi (cioè profonda o superficiale). Tuttavia, in questo caso, non c'è bisogno per noi (BCL) per definirlo. Definiamo solo astrazioni nel BCL quando v'è la necessità di scambiare casi tipizzati come quella astrazione tra le biblioteche indipendenti.

David Kean (BCL Team)

Credo che la domanda "perché" è inutile. C'è un sacco di interfacce / classi / ecc ... che è molto utile, ma non fa parte della libreria di base .NET Frameworku.

Ma, soprattutto si può fare da soli.

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

E 'abbastanza facile da scrivere il interfacciare te se ne avete bisogno:

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

Dopo aver letto di recente il Perché Copia di un oggetto è una cosa terribile da fare? , credo che questa domanda ha bisogno di ulteriori clafirication. Altre risposte qui forniscono buoni consigli, ma ancora la risposta non è completa - perché no ICloneable<T>

  1. Utilizzo

    Quindi, si dispone di una classe che implementa. Mentre in precedenza si ha un metodo che voleva ICloneable, ha ora ad essere generico ad accettare ICloneable<T>. Si avrebbe bisogno di modificarla.

    Quindi, si avrebbe potuto ottenere un metodo che controlla se un is ICloneable oggetto. E adesso? Non si può fare is ICloneable<> e come non si conosce il tipo di oggetto in fase di compilazione tipo, non è possibile effettuare il metodo generico. Primo problema reale.

    Quindi è necessario avere sia ICloneable<T> e ICloneable, l'ex attuazione quest'ultimo. Così un realizzatore avrebbe bisogno di implementare entrambi i metodi - object Clone() e T Clone(). No, grazie, abbiamo già abbastanza divertente con IEnumerable.

    Come già sottolineato, c'è anche la complessità di eredità. Mentre covarianza può sembrare a risolvere questo problema, un tipo derivato deve attuare ICloneable<T> del proprio tipo, ma v'è già un metodo con la stessa firma (= parametri, fondamentalmente) - la Clone() della classe base. Effettuare la nuova interfaccia metodo clone esplicito è inutile, si perde il vantaggio che cercavi quando si creano ICloneable<T>. Quindi aggiungere la parola chiave new. Ma non dimenticate che si sarebbe anche bisogno di ignorare la Clone() classe di base (l'attuazione deve rimanere uniforme per tutte le classi derivate, cioè di restituire lo stesso oggetto da ogni metodo clone, quindi il metodo di base clone deve essere virtual) ! Ma, purtroppo, non è possibile sia override e new metodi con la stessa firma. Scegliendo la prima parola chiave, che ci si perde l'obiettivo che si voleva avere quando si aggiunge ICloneable<T>. Chossing il secondo, che ci si rompe l'interfaccia stessa, rendendo i metodi che dovrebbero fare lo stesso rendimento diversi oggetti.

  2. Punto

    Si vuole ICloneable<T> per il comfort, ma il comfort non è quello che le interfacce sono progettate per, il loro significato è (in OOP genere) di unificare il comportamento degli oggetti (anche se in C #, si è limitato a unificare il comportamento esterno, ad esempio, i metodi e proprietà, non il loro funzionamento).

    Se il primo motivo per cui non ti ha ancora convinto, si potrebbe obiettare che ICloneable<T> potrebbe anche funzionare in modo restrittivo, per limitare il tipo restituito dal metodo clone. Tuttavia, brutto programmatore può realizzare ICloneable<T> dove T non è il tipo che lo implementa. Così, per raggiungere il vostro restrizione, è possibile aggiungere un bel vincolo per il parametro generico:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Certamente più restrittiva che quello senza where, ancora non può limitare tale T è il tipo che implementa l'interfaccia (si può derivare da ICloneable<T> di tipo diverso che lo implementa).

    Si vede, anche questo scopo non potrebbe essere raggiunto (il ICloneable originale non riesce anche a questo, nessuna interfaccia può veramente limitare il comportamento della classe che implementa).

Come si può vedere, questo dimostra rendendo l'interfaccia generica è sia difficile da attuare pienamente e anche molto non necessari e inutile.

Ma torniamo alla domanda, che cosa realmente cercate è quello di avere la comodità quando la clonazione di un oggetto. Ci sono due modi per farlo:

Metodi aggiuntivi

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

Questa soluzione offre sia il comfort e il comportamento destinato agli utenti, ma è anche troppo lungo da implementare. Se non volevamo avere il metodo di "comodo" restituire il tipo di corrente, è molto più facile averesolo public virtual object Clone().

Quindi cerchiamo di vedere la soluzione "finale" - ciò che in C # è davvero intented a darci conforto?

metodi di estensione!

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

E 'il nome Copia non entrare in collisione con gli attuali metodi Clone (compilatore preferisce propri metodi dichiarati del tipo rispetto a quelli di estensione). Il vincolo class è lì per la velocità (non richiede controllo nullo, ecc.).

Spero che questo chiarisce il motivo per cui non fare ICloneable<T>. Tuttavia, si raccomanda di non implementare ICloneable a tutti.

Anche se la questione è molto vecchio (5 anni da scrivere questo risposte :) ed è stato già risposto, ma ho trovato questo articolo risponde alla domanda abbastanza bene, controllare è qui

Modifica

Ecco la citazione di questo articolo che risponde alla domanda (assicuratevi di leggere l'articolo completo, include altre cose interessanti):

  

Ci sono molti riferimenti sul puntamento Internet a un post del blog 2003   da Brad Abrams - al tempo impiegato presso Microsoft - in cui alcuni   pensieri su ICloneable sono discussi. Il blog può essere trovato   a questo indirizzo: Implementazione ICloneable . Nonostante la fuorviante   titolo, questo blog non chiamate ad attuare ICloneable, principalmente   a causa della poca confusione / profondità. Articolo finisce in un rettilineo   suggerimento: se avete bisogno di un meccanismo di clonazione, definire il proprio clone, o   Copiare la metodologia, e garantire che si documentare chiaramente se si tratta di un   profonda o superficiale copia. Un modello adatto è: public <type> Copy();

Un grosso problema è che non potevano limitare T essere la stessa classe. Fore esempio quello che ti impedisce di fare questo:

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

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

}

Hanno bisogno di una restrizione di parametri come:

interfact IClonable<T> where T : implementing_type

E 'una domanda molto buona ... Si potrebbe fare il vostro proprio, però:

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

Andrey dice che è considerato un cattivo API, ma non ho sentito nulla di questa interfaccia diventando obsoleto. E che sarebbe rompere tonnellate di interfacce ... Il metodo Clone deve eseguire una copia. Se l'oggetto fornisce anche copia completa, un clone sovraccarico (bool profonda) può essere utilizzato.

EDIT:. Reticolo Io uso per "clonazione" di un oggetto, sta passando un prototipo nel costruttore

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

Questo rimuove tutte le possibili situazioni di implementazione di codice ridondante. BTW, parlando delle limitazioni di ICloneable, non è davvero fino a l'oggetto stesso a decidere se un clone superficiale o profondo clone, o anche un clone in parte superficiale / in parte in profondità, devono essere eseguiti? Dovremmo veramente a cuore, a condizione che i lavori oggetto come previsto? In alcune occasioni, una buona implementazione Clone potrebbe benissimo includere sia la clonazione superficiale e profondo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top