2015-05-06 17 views
10

Ich schreibe ein Programm, das C# schreibt, das schließlich in eine Anwendung kompiliert wird. Ich möchte, dass jeder der generierten Typen eine "Deep Clone" -Funktion bereitstellt, die den gesamten Baum der Daten kopiert. Das heißt, ich jemand möchte in der Lage sein zu tun:Ist es möglich, das Muster "virtuelles Konstruktor" in C# ohne Umwandlungen zu implementieren?

var x = new Base(); // Base has public virtual Base DeepClone() { ... } 
var y = new Derived(); // Derived overrides DeepClone 
Base a = x.DeepClone(); 
Base b = y.DeepClone(); 
// Derived c = x.DeepClone(); // Should not compile 
Derived d = y.DeepClone(); // Does not compile, DeepClone returns Base 

statt

var x = new Base(); 
var y = new Derived(); 
Base a = x.DeepClone(); 
Base b = y.DeepClone(); 
// Derived c = x.DeepClone(); // Should not compile 
Derived d = (Derived)y.DeepClone(); 

Allerdings ist C# nicht zulassen, dass Sie dies in einer einfachen Überschreibung tun; Die Außerkraftsetzungen müssen den gleichen Typ wie den deklarierten Typ auf der Basis zurückgeben.

Da ich Code schreibe, der sowieso boilerplate stempelt, gibt es etwas, das ich erzeugen kann, damit der erste Block kompilieren kann? Ich habe etwas wie das Folgende versucht:

abstract class Base 
{ 
    public abstract Base DeepClone(); 
} 

class Base2 : Base 
{ 
    int Member { get; set; } 

    public Base2() { /* empty on purpose */ } 
    public Base2(Base2 other) 
    { 
     this.Member = other.Member; 
    } 

    public override Base2 DeepClone() 
    { 
     return new Base2(this); 
    } 
} 

sealed class Derived : Base2 
{ 
    string Member2 { get; set; } 

    public Derived() { /* empty on purpose */ } 
    public Derived(Derived other) 
     : base(other) 
    { 
     this.Member2 = other.Member2; 
    } 

    public override Derived DeepClone() 
    { 
     return new Derived(this); 
    } 
} 

aber das kompiliert nicht, weil die Überschreibungen nicht übereinstimmen. Ich habe auch versucht, die Methode von der Basis zu überschreiben und sie mit dem Schlüsselwort "new" zu verstecken, aber das hat auch nicht funktioniert.

+1

In C# gibt es keine Möglichkeit, dass eine Klasse sich unglücklicherweise auf ihren eigenen Typ bezieht. Es gibt hacky Wege wie das seltsam wiederkehrende Vorlagenmuster, das Ihnen die Standardarbeit erspart, aber nicht idiotensicher ist. –

+0

@Asad: Hier sind keine Generika beteiligt. –

+0

"Es gibt keine Generika hier beteiligt" Ja, aber dieses Problem ist konzeptionell einer der Generika. Sie möchten eine 'DeepClone'-Methode, die einen beliebigen von 'Base' abgeleiteten Typ akzeptiert, einige Operationen darauf ausführt und denselben Typ zurückgibt. –

Antwort

4

Ja, es ist machbar, aber Sie müssen Ihre abstrakte Methode von öffentlich zu geschützt werden, dann machen Sie eine öffentliche nicht abstrakte Funktion, die nur die geschützte Methode aufruft. Die abgeleiteten Klassen müssen nur die geschützte Funktion implementieren und können die öffentliche Funktion überschatten, indem sie die vom Client durchgeführte Umwandlung durchführen.

abstract class Base 
{ 
    public Base DeepClone() 
    { 
     return CloneInternal(); 
    } 

    protected abstract Base CloneInternal(); 
} 

class Base2 : Base 
{ 
    int Member { get; set; } 

    public Base2() { /* empty on purpose */ } 
    public Base2(Base2 other) 
    { 
     this.Member = other.Member; 
    } 

    new public Base2 DeepClone() 
    { 
     return (Base2)CloneInternal(); 
    } 

    protected override Base CloneInternal() 
    { 
     return new Base2(this); 
    } 
} 

sealed class Derived : Base2 
{ 
    string Member2 { get; set; } 

    public Derived() { /* empty on purpose */ } 
    public Derived(Derived other) 
     : base(other) 
    { 
     this.Member2 = other.Member2; 
    } 

    new public Derived DeepClone() 
    { 
     return (Derived)CloneInternal(); 
    } 

    protected override Base CloneInternal() 
    { 
     return new Derived(this); 
    } 
} 
+1

Alle Probleme der Informatik können gelöst werden, indem man eine indirekte Ebene hinzufügt, nicht wahr? :) –

+0

@BillyONeal Sie sehen dieses Muster über das gesamte .NET Framework in den neueren Klassen (.NET 2.0+). Zum Beispiel [BindinigList .AddNew ('] (https://msdn.microsoft.com/en-us/library/ms132687 (v ​​= vs.110) .aspx) und [' BindingList .AddNewCore'] (https : //msdn.microsoft.com/en-us/library/ms132688 (v = vs.110) .aspx) –

3

Hier ist eine Möglichkeit, dies zu tun, die kein Gießen beinhaltet. Es erlaubt Sie nicht new Base() von Ihrem ersten Schnipsel zu tun, die keinen Sinn machen, weil es abstrakt ist, aber der Rest funktioniert:

interface Base 
{ 
    Base DeepClone(); 
} 

abstract class Base<T>: Base where T: Base<T> 
{ 
    public abstract T DeepClone(); 
    Base Base.DeepClone() { 
     return DeepClone(); 
    } 
} 

class Base2 : Base<Base2> 
{ 
    public override Base2 DeepClone() 
    { 
     return new Base2(); 
    } 
} 

Dann in Ihrer Main Methode:

public static void Main() 
{ 
    var y = new Base2(); // Base2 overrides DeepClone 
    Base b = y.DeepClone(); 
    Base2 c = y.DeepClone(); // Compiles an works 
} 
+0

Dieses Muster wird das [seltsam wiederkehrende Vorlagenmuster] (http://en.wikipedia.org/wiki) genannt/Curiously_recurring_template_pattern) –

+0

@ScottChamberlain Ja, das ist richtig, ich habe das in den obigen Kommentaren erwähnt.Es ist in C# nicht idiotensicher, aber es spart Ihnen ein wenig Tipparbeit. –