Frage

Gibt es einen bestimmten Grund, warum eine generische ICloneable<T> nicht existiert?

Es wäre viel angenehmer sein, wenn ich es nicht werfen müsste jedes Mal wenn ich klonen etwas.

War es hilfreich?

Lösung

ICloneable ist eine schlechte API jetzt betrachtet, da es nicht angibt, ob das Ergebnis eine tiefe oder eine flache Kopie ist. Ich denke, das ist, warum sie nicht über diese Schnittstelle verbessern.

Sie können sich wahrscheinlich eine typisierte Klonen Extension-Methode tun, aber ich denke, es wäre einen anderen Namen erfordern, da Erweiterungsmethoden weniger Priorität als Originale.

Andere Tipps

Neben Andrey Antwort (die ich mit einigen, +1) - wenn ICloneable ist getan, können Sie auch explizite Implementierung wählen, um die Öffentlichkeit Clone() ein typisierte Objekt zu machen zurückgeben:

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

Natürlich gibt es ein zweites Problem mit einem generischen ICloneable<T> -. Vererbung

Wenn ich:

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

Und ich implementiert ICloneable<T>, dann implementiere ich ICloneable<Foo>? ICloneable<Bar>? Sie starten schnell viele identische Schnittstellen Implementierung ... Vergleichen Sie bis zu einem gegossenen ... und ist es wirklich so schlimm?

Ich frage müssen, was genau würden Sie tun, mit der Schnittstelle außer es umsetzen? Schnittstellen sind in der Regel nur dann sinnvoll, wenn Sie es gegossen (dh diese Klasse einen Träger ‚IBar‘), oder haben Parameter oder Setter, die es nehmen (dh ich nehme eine ‚IBar‘). Mit ICloneable - gingen wir durch den gesamten Rahmen und nicht eine einzige Nutzung überall zu finden, die etwas anderes als eine Implementierung davon war. Wir haben auch nicht jede Nutzung in der ‚realen Welt‘ zu finden, die auch etwas anderes tut, als es umzusetzen (die ~ 60.000 Anwendungen, die wir Zugriff haben).

Nun, wenn Sie nur ein Muster erzwingen möchten, dass Sie Ihre ‚klonbar‘ Objekte implementieren möchten, ist das eine ganz feine Nutzung - und gehen Sie voran. Sie können auch genau entscheiden, was „Klonen“ für Sie bedeutet (dh tief oder flach). Doch in diesem Fall gibt es keine Notwendigkeit für uns (die BCL) zu definieren. Wir haben leider nur Abstraktionen in der BCL definieren, wenn es notwendig ist, Instanzen wie die Abstraktion zwischen unabhängigen Bibliotheken getippt auszutauschen.

David Kean (BCL-Team)

Ich denke, die Frage „Warum“ unnötig ist. Es gibt eine Vielzahl von Schnittstellen / Klassen / etc ..., die sehr nützlich ist, ist aber nicht Teil von .NET Frameworku Basisbibliothek.

Aber vor allem können Sie es selbst tun.

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

Es ist ziemlich einfach zu schreiben die Schnittstelle selbst wenn Sie es brauchen:

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

Nachdem vor kurzem den Artikel Warum ein Objekt Kopieren ist eine schreckliche Sache zu tun? ich denke, diese Frage zusätzliche clafirication muss. Andere Antworten hier bieten gute Ratschläge, aber immer noch die Antwort ist nicht vollständig - warum nicht ICloneable<T>

  1. Verwendung

    Also, Sie haben eine Klasse, die es implementiert. Während früher eine Methode hatte die ICloneable wollte, hat es nun generisch sein ICloneable<T> zu akzeptieren. Sie würden es bearbeiten müssen.

    Dann Sie eine Methode haben könnten bekommen, dass, wenn ein Objekt is ICloneable prüft. Was jetzt? Sie können nicht is ICloneable<> tun und wie Sie die Art des Objekts bei der Kompilierung-Typ nicht kennen, können Sie nicht die Methode generisch machen. Erstes echtes Problem.

    So können Sie sowohl ICloneable<T> und ICloneable haben müssen, die ersterer umzusetzen. Somit wäre ein Implementierer müssen beide Methoden implementieren - object Clone() und T Clone(). Nein, danke, wir haben schon genug Spaß mit IEnumerable.

    Wie bereits erwähnt, gibt es auch die Komplexität der Vererbung. Während Kovarianz scheinen mag, dieses Problem zu lösen, muss ein abgeleiteter Typ ICloneable<T> seine eigenen Art implementieren, aber es ist bereits ein Verfahren mit der gleichen Signatur (= Parameter, im Grunde) - die Clone() der Basisklasse. Ihre neue Klon-Methode Schnittstelle explizit zu machen ist sinnlos, werden Sie den Vorteil, den Sie gesucht bei der Erstellung von ICloneable<T> verlieren. So fügen Sie das new Schlüsselwort. Aber vergessen Sie nicht, dass Sie auch die Basisklasse Clone() brauchen würden, außer Kraft zu setzen (die Umsetzung hat einheitlich für alle abgeleiteten Klassen zu bleiben, dh das gleiche Objekt aus jeder Klon-Methode zurück, so dass die Basis-Klon-Methode hat virtual sein) ! Aber leider kann man nicht beide override und new Methoden mit derselben Signatur. Die Wahl des ersten Schlüsselwort, dann würden Sie das Ziel, verlieren Sie haben wollten, wenn ICloneable<T> hinzufügen. die zweite chossing, dann würden Sie die Schnittstelle selbst brechen, so dass Methoden, die die gleiche Rendite verschiedene Objekte tun sollten.

  2. Punkt

    Sie wollen ICloneable<T> für Komfort, aber Komfort ist nicht das, was Schnittstellen für ausgelegt sind, ihre Bedeutung ist (im allgemeinen OOP) das Verhalten von Objekten zu vereinigen (obwohl in C #, es Vereinheitlichung der äußeren Verhalten beschränkt ist, zB die Verfahren und Eigenschaften, nicht ihre Funktionsweise).

    Wenn der erste Grund, warum Sie noch nicht überzeugt hat, könnten Sie die ICloneable<T> Objekt auch restriktiv funktionieren könnte, den Typen aus der Klon-Methode zurück zu begrenzen. Allerdings kann böse Programmierer implementieren ICloneable<T> wobei T nicht der Typ ist, der es umsetzt. So erreichen Sie Ihre Einschränkung, können Sie eine schöne Einschränkung der generischen Parameter hinzuzufügen:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Sicherlich restriktiver, dass das eine ohne where, man kann immer noch nicht einschränken, dass T ist der Typ, der die Schnittstelle implementiert (Sie von ICloneable<T> von einem anderen Typ ableiten kann, die es implementiert).

    Sie sehen, auch könnte dieser Zweck nicht erreicht werden (der ursprüngliche ICloneable versagt auch diese kann keine Schnittstelle wirklich das Verhalten der implementierenden Klasse begrenzen).

Wie Sie sehen können, das beweist, was die generische Schnittstelle sowohl hart ist vollständig umzusetzen und auch wirklich nicht mehr benötigt und nutzlos.

Aber zurück zu der Frage, was Sie wirklich suchen, ist Komfort zu haben, wenn ein Objekt klonen. Es gibt zwei Möglichkeiten, es zu tun:

Weitere Methoden

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

Diese Lösung bietet sowohl Komfort als auch beabsichtigtes Verhalten für die Nutzer, aber es ist auch zu lang umzusetzen. Wenn wir nicht die „komfortabel“ Methode haben wollten den aktuellen Typs Rückkehr, ist es viel leichter zu habennur public virtual object Clone().

Lassen Sie uns also die „ultimative“ Lösung sehen - was in C # intented ist wirklich zu geben, uns zu trösten?

Erweiterungsmethoden!

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

Es ist dem Namen Kopieren nicht mit den aktuellen Clone Methoden kollidieren (Compiler bevorzugt eigene deklarierten Methoden über Verlängerung diejenigen, die den Typ). Die class Einschränkung gibt es für die Geschwindigkeit (nicht NULL-Prüfung usw. erfordern.).

Ich hoffe, dass dies der Grund stellt klar, warum nicht ICloneable<T> zu machen. Es wird jedoch empfohlen, nicht ICloneable überhaupt umzusetzen.

Auch wenn die Frage ist sehr alt (5 Jahre ab schreibe diese Antworten :) und wurde bereits beantwortet, aber ich fand dieser Artikel beantwortet die Frage ganz gut, überprüfen Sie es hier

EDIT:

Hier ist das Zitat aus dem Artikel, der die Frage beantwortet (stellen Sie sicher, den vollständigen Artikel zu lesen, enthält es andere interessante Dinge):

  

Es gibt viele Hinweise auf dem Internet zeigt auf eine 2.003 Blog-Post   von Brad Abrams - zu der Zeit bei Microsoft beschäftigt - in denen einige   Gedanken über ICloneable diskutiert. Der Blog-Eintrag gefunden werden kann   an dieser Adresse: implementieren ICloneable . Trotz der irreführenden   Titel dieser Blog-Eintrag Anrufe nicht ICloneable zu implementieren, vor allem   wegen flach / tief Verwirrung. Artikel endet in einer geraden   Vorschlag: Wenn Sie einen Klonens Mechanismus benötigen, Ihre eigenen Klon definieren oder   Kopieren Methodik und stellen Sie sicher, dass Sie klar dokumentieren, ob es sich um eine   tiefe oder flache Kopie. Ein entsprechendes Muster ist: public <type> Copy();

Ein großes Problem ist, dass sie T nicht einschränken könnten die gleiche Klasse. Fore Beispiel, was Sie würde verhindern, dass dies zu tun:

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

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

}

Sie benötigen einen Parameter Einschränkung wie:

interfact IClonable<T> where T : implementing_type

Es ist eine sehr gute Frage ... Sie könnten Ihre eigenen machen, aber:

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

Andrey sagt, dass es eine schlechte API betrachtet, aber ich habe nichts über diese Schnittstelle gehört veraltet werden. Und das würde jede Menge Schnittstellen brechen ... Die Clone-Methode soll eine flache Kopie auszuführen. Wenn auch das Objekt tiefe Kopie bereitstellt, kann eine überladene Clone (Bool tief) verwendet werden.

EDIT:. Pattern i für "Klonen" ein Objekt verwenden, wird ein Prototyp in den Konstruktor übergeben

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

Dies entfernt alle möglichen redundanten Code-Implementierung Situationen. BTW, im Gespräch über die Grenzen der ICloneable, ist es nicht wirklich an das Objekt selbst zu entscheiden, ob ein flacher Klon oder tiefe Klon oder sogar ein teilweise flach / teils tiefer Klon, durchgeführt werden sollte? Sollten wir wirklich egal, solange die Objekt funktioniert wie beabsichtigt? In einigen Fällen könnte eine gute Clone Implementierung ist sehr gut sowohl flache und tiefe Klonen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top