2008-09-25 2 views
6

zu verwenden Die einfache Demo unten erfasst, was ich versuche zu tun. Im realen Programm muss ich den Objektinitialisiererblock verwenden, da er eine Liste in einem LINQ to SQl select -Ausdruck liest, und es gibt einen Wert, den ich von der Datenbank ablesen und auf dem Objekt speichern möchte, aber das Objekt hat keine einfache Eigenschaft, die ich für diesen Wert festlegen kann. Stattdessen hat es einen XML-Datenspeicher.Gibt es eine Möglichkeit, eine Erweiterungs-Methode in einem Objektinitialisiererblock in C#

Es sieht so aus, als ob ich im Objektinitialisiererblock keine Erweiterungsmethode aufrufen kann und dass ich keine Eigenschaft mit Erweiterungsmethoden anhängen kann.

Also habe ich Pech mit diesem Ansatz? Die einzige Alternative scheint zu sein, den Besitzer der Basisklasse zu überreden, ihn für dieses Szenario zu modifizieren.

Ich habe eine bestehende Lösung, wo ich BaseDataObject ableiten, aber das hat auch Probleme, die in diesem einfachen Beispiel nicht angezeigt werden. Die Objekte werden beibehalten und als BaseDataObject wiederhergestellt - die Umwandlungen und Tests werden komplex.

public class BaseDataObject 
{ 

// internal data store 
private Dictionary<string, object> attachedData 
              = new Dictionary<string, object>(); 

public void SetData(string key, object value) 
{ 
    attachedData[key] = value; 
} 

public object GetData(string key) 
{ 
    return attachedData[key]; 
} 

public int SomeValue { get; set; } 
public int SomeOtherValue { get; set; } 

} 

public static class Extensions 
{ 
    public static void SetBarValue(this BaseDataObject dataObject, 
             int   barValue) 
    { 
     /// Cannot attach a property to BaseDataObject? 
     dataObject.SetData("bar", barValue); 
    } 
} 

public class TestDemo 
{ 

public void CreateTest() 
{ 
    // this works 
    BaseDataObject test1 = new BaseDataObject 
     { SomeValue = 3, SomeOtherValue = 4 }; 

    // this does not work - it does not compile 
    // cannot use extension method in the initialiser block 
    // cannot make an exension property 
    BaseDataObject test2 = new BaseDataObject 
    { SomeValue = 3, SomeOtherValue = 4, SetBarValue(5) }; 
} 
} 

Eine der Antworten (von mattlant) eine Fluent Interface Design Extension-Methode schlägt mit. Beispiel:

// fluent interface style 
    public static BaseDataObject SetBarValueWithReturn(
this BaseDataObject dataObject, int barValue) 
    { 
     dataObject.SetData("bar", barValue); 
     return dataObject; 
    } 

     // this works 
     BaseDataObject test3 = (new BaseDataObject 
{ SomeValue = 3, SomeOtherValue = 4 }).SetBarValueWithReturn(5); 

Aber funktioniert das in einer LINQ-Abfrage?

+0

Soll die Set-Methode vom BaseDataObject entfernt werden? Ich würde dringend vorschlagen, einfach Unterklasse mit Get/Set Eigenschaften zu verwenden. – Tigraine

Antwort

3

Noch besser:

public static T SetBarValue<T>(this T dataObject, int barValue) 
     where T : BaseDataObject 
    { 
     dataObject.SetData("bar", barValue); 
     return dataObject; 
    } 

und Sie können für abgeleitete Typen von BaseDataObject Ketten Methoden ohne Abgüsse diese Erweiterung Methode verwenden und die reale Art erhalten, wenn sie in einem var Feld oder anonymen Typ abgeleitet.

4

Object Initializers sind nur syntaktische Zucker, die einen cleveren Compiler benötigen, und seit der aktuellen Implementierung können Sie keine Methoden im Initialisierer aufrufen.

var x = new BaseDataObject { SomeValue = 3, SomeOtherValue = 4 }; 

Wird Compiler etwas wie diese:

BaseDataObject tempObject = new BaseDataObject(); 
tempObject.SomeValue = 3; 
tempObject.SomeOtherValue = 4; 
BaseDataObject x = tempObject; 

Der Unterschied besteht darin, dass es keine Probleme bei der Synchronisierung sein kann. Die Variable x get erhält das vollständig zugewiesene BaseDataObject auf einmal. Sie können sich während der Initialisierung nicht mit dem Objekt anlegen.

könnten Sie rufen die Erweiterungsmethode nach der Objekterstellung:

var x = new BaseDataObject { SomeValue = 3, SomeOtherValue = 4 }; 
x.SetBarValue() 

Sie könnten SetBarValue ändern eine Eigenschaft mit get/set zu sein, die bei der Initialisierung zugewiesen werden können:

public int BarValue 
{ 
    set 
    { 
     //Value should be ignored 
    } 
} 

Or Sie könnten Unterklasse/verwenden Sie das Fassadenmuster, um die Methode auf Ihr Objekt hinzuzufügen:

public class DataObjectWithBarValue : BaseDataObject 
{ 
    public void BarValue 
    { 
     set 
     { 
      SetData("bar", value); 
     } 
     get 
     { 
      (int) GetData("bar"); 
     } 
    } 
} 
4

Nein, aber man könnte dies tun ....:

BaseDataObject test2 = (new BaseDataObject { SomeValue = 3, SomeOtherValue = 4}).SetBarValue(5); 

und haben Ihre Erweiterung das Objekt wie Linq zurückgibt.

EDIT: Das war ein guter Gedanke bis ich wieder gelesen habe und sah, dass die Basisklasse von einer dritten Person entwickelt wurde: aka Sie haben nicht den Code. Andere hier haben eine korrekte Lösung gepostet.

+0

Müssen nett sein, um die hohen rep Punkte zu haben, gehen Sie einfach und löschen Sie Ihre falsche Antwort, anstatt Ihren Fehler zu besitzen:/(Sie wissen wer Sie sind;)) – mattlant

+0

Es sollte noch funktionieren. LINQ-Anbieter interessieren sich nur für den Teil einer Select-Abfrage, auf den die zugeordneten Elemente verwiesen werden. –

+0

Ich habe meine gelöscht, weil ich es ein Duplikat von Ihnen war. –

0

Erweiterung der Klasse eine Möglichkeit? Dann können Sie problemlos die gewünschte Eigenschaft hinzufügen.

dass Andernfalls können Sie eine neue Klasse erstellen, die ähnliche Eigenschaften hat, die einfach zu einer privaten Instanz der Klasse, die Sie interessiert sind rufen Sie zurück.

1
static T WithBarValue<T>(this T dataObject, int barValue) 
     where T : BaseDataObject 
{ dataObject.SetData("bar", barValue);  
    return dataObject; 
} 

var x = new BaseDataObject{SomeValue=3, OtherValue=4}.WithBarValue(5); 
0

Rechts, von den Beantworter gelernt zu haben, die kurze Antwort zu "Gibt es eine Möglichkeit, eine Erweiterungs-Methode in einem Objektinitialisiererblock in C# zu verwenden?" ist „Nr

Die Art und Weise, dass ich schließlich das Problem gelöst, dass ich konfrontiert (ähnlich, aber komplizierter, dass das Spielzeug Problem, das ich hier gestellt) war ein Hybrid-Ansatz wie folgt:

I erstellt eine Unterklasse, z

public class SubClassedDataObject : BaseDataObject 
{ 
    public int Bar 
    { 
     get { return (int)GetData("bar"); } 
     set { SetData("bar", value); } 
    } 
} 

die in LINQ, die Initialisierung Block sieht aus wie

SubClassedDataObject testSub = new SubClassedDataObject 
    { SomeValue = 3, SomeOtherValue = 4, Bar = 5 }; 

Aber der Grund, dass ich in erster Linie diesen Ansatz nicht gefallen haben gut funktioniert, ist, dass diese Objekte in XML gestellt werden und kommen wieder als BaseDataObject ausgeben, und das Zurückwerfen würde ein Ärgernis sein, eine unnötige Datenkopie, und würde zwei Kopien desselben Objekts ins Spiel bringen.

Im Rest des Codes, ignorierte ich die Unterklassen und verwendete Erweiterungsmethoden:

public static void SetBar(this BaseDataObject dataObject, int barValue) 
    { 
     dataObject.SetData("bar", barValue); 
    } 
    public static int GetBar(this BaseDataObject dataObject) 
    { 
     return (int)dataObject.GetData("bar"); 
    } 

Und es funktioniert gut.

Verwandte Themen