Esiste un modo per utilizzare un metodo di estensione in un blocco inizializzatore oggetto in C #

StackOverflow https://stackoverflow.com/questions/132245

Domanda

La semplice demo qui sotto cattura ciò che sto cercando di fare. Nel programma reale, devo usare il blocco inizializzatore oggetto poiché sta leggendo un elenco in un'espressione di selezione LINQ to SQL, e c'è un valore che voglio leggere dal database e archiviare sull'oggetto, ma l'oggetto non ha una proprietà semplice che posso impostare per quel valore. Invece ha un archivio di dati XML.

Sembra che non riesca a chiamare un metodo di estensione nel blocco inizializzatore oggetto e che non riesco a collegare una proprietà usando i metodi di estensione.

Quindi sono sfortunato con questo approccio? L'unica alternativa sembra essere quella di convincere il proprietario della classe base a modificarla per questo scenario.

Ho una soluzione esistente in cui eseguo la sottoclasse di BaseDataObject, ma anche questo ha problemi che non compaiono in questo semplice esempio. Gli oggetti vengono mantenuti e ripristinati come BaseDataObject: i cast e i test diventerebbero complessi.

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

Una delle risposte (da Mattlant) suggerisce l'utilizzo di un metodo di estensione in stile interfaccia fluente. per esempio:.

// 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);

Ma funzionerà in una query LINQ?

È stato utile?

Soluzione

Ancora meglio:

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

e puoi usare questo metodo di estensione per tipi derivati ??di BaseDataObject per eseguire il chain chain senza cast e preservare il tipo reale quando inferito in un campo var o tipo anonimo.

Altri suggerimenti

Gli inizializzatori di oggetti sono solo zucchero sintattico che richiede un compilatore intelligente e, a partire dall'attuale implementazione, non è possibile chiamare i metodi nell'inizializzatore.

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

Otterrà il compilatore in qualcosa del genere:

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

La differenza è che non possono esserci problemi di sincronizzazione. Alla variabile x get è stato assegnato il BaseDataObject completamente assegnato in una sola volta, non puoi scherzare con l'oggetto durante l'inizializzazione.

Potresti semplicemente chiamare il metodo di estensione dopo la creazione dell'oggetto:

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

È possibile modificare SetBarValue in una proprietà con get / set che può essere assegnata durante l'inizializzazione:

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

Oppure, puoi sottoclassare / usare il modello di facciata per aggiungere il metodo al tuo oggetto:

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

No, ma potresti farlo ....:

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

E la tua estensione restituirà l'oggetto come Linq Does.

EDIT: Questa è stata una buona idea fino a quando non ho letto e visto che la classe base è stata sviluppata da una terza persona: alias non hai il codice. Altri qui hanno pubblicato una soluzione corretta.

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

L'estensione della classe è una possibilità? Quindi potresti facilmente aggiungere la proprietà di cui hai bisogno.

In caso contrario, puoi creare una nuova classe con proprietà simili che richiamano semplicemente un'istanza privata della classe che ti interessa.

Esatto, dopo aver appreso dai risponditori, la risposta breve a " Esiste un modo per usare un metodo di estensione in un blocco di inizializzazione di oggetti in C #? " è " No. "

Il modo in cui alla fine ho risolto il problema che ho affrontato (simile, ma più complesso del problema del giocattolo che ho posto qui) è stato un approccio ibrido, come segue:

Ho creato una sottoclasse, ad esempio

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

Che funziona bene in LINQ, il blocco di inizializzazione sembra

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

Ma il motivo per cui questo approccio non mi è piaciuto in primo luogo è che questi oggetti sono messi in XML e ritornano come BaseDataObject, e il ricollocamento sarebbe stato un fastidio, una copia di dati non necessaria e sarebbe metti in gioco due copie dello stesso oggetto.

Nel resto del codice, ho ignorato le sottoclassi e ho usato i metodi di estensione:

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

E funziona bene.

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