Est-il possible d'utiliser une méthode d'extension dans un bloc d'initialisation d'objet en C #

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

Question

La simple démo ci-dessous illustre ce que j'essaie de faire. Dans le programme réel, je dois utiliser le bloc d’initialisation d’objet car il lit une liste dans une expression de sélection LINQ to SQL. Il existe une valeur que je veux lire dans la base de données et la stocker sur cet objet, mais n'a pas une propriété simple que je peux définir pour cette valeur. Au lieu de cela, il possède un magasin de données XML.

Il semble que je ne puisse pas appeler une méthode d'extension dans le bloc d'initialisation d'objet et que je ne puisse pas attacher une propriété à l'aide de méthodes d'extension.

Alors, suis-je à court de chance avec cette approche? La seule alternative semble être de persuader le propriétaire de la classe de base de la modifier pour ce scénario.

J'ai une solution existante dans laquelle je sous-classe BaseDataObject, mais cela pose également des problèmes qui n'apparaissent pas dans cet exemple simple. Les objets sont conservés et restaurés en tant que BaseDataObject - les conversions et les tests deviendraient complexes.

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

L’une des réponses (proposée par Mattlant) suggère d’utiliser une méthode d’extension de type interface fluide. par exemple:

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

Mais cela fonctionnera-t-il dans une requête LINQ?

Était-ce utile?

La solution

Encore mieux:

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

et vous pouvez utiliser cette méthode d'extension pour les types dérivés de BaseDataObject pour enchaîner les méthodes sans jeter et conserver le type réel lorsqu'il est inféré dans un champ var ou un type anonyme.

Autres conseils

Les initialiseurs d'objet sont juste un sucre syntaxique qui nécessite un compilateur astucieux. A partir de l'implémentation actuelle, vous ne pouvez pas appeler de méthodes dans l'initialiseur.

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

obtiendra le compilateur à quelque chose comme ceci:

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

La différence est qu’il ne peut y avoir de problème de synchronisation. La variable x get est affectée immédiatement à l'attribut BaseDataObject complètement assigné, vous ne pouvez pas jouer avec l'objet pendant son initialisation.

Vous pouvez simplement appeler la méthode d'extension après la création de l'objet:

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

Vous pouvez définir SetBarValue comme une propriété avec get / set pouvant être affectée lors de l'initialisation:

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

Vous pouvez également sous-classer / utiliser le motif de façade pour ajouter la méthode à votre objet:

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

Non, mais vous pouvez le faire ....:

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

et demandez à votre extension de renvoyer l'objet comme Linq Does.

EDIT: C’était une bonne idée jusqu’à ce que j’aie relu et que la classe de base ait été développée par une troisième personne: c’est-à-dire que vous n’avez pas le code. D'autres ici ont posté une solution correcte.

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

Est-ce que l'extension de la classe est une possibilité? Ensuite, vous pourrez facilement ajouter la propriété dont vous avez besoin.

Si vous ne le faites pas, vous pouvez créer une nouvelle classe ayant des propriétés similaires, qui rappelle simplement une instance privée de la classe qui vous intéresse.

Après avoir appris des réponses, la réponse courte à "Existe-t-il un moyen d'utiliser une méthode d'extension dans un bloc d'initialisation d'objet en C #?" est " Non. "

La façon dont j'ai finalement résolu le problème auquel je faisais face (similaire, mais plus complexe que le problème des jouets que je posais ici) était une approche hybride, comme suit:

J'ai créé une sous-classe, par exemple

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

Ce qui fonctionne bien dans LINQ, le bloc d’initialisation ressemblant à

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

Mais la raison pour laquelle je n’ai pas aimé cette approche au départ, c’est que ces objets sont mis en XML et sortent en tant que BaseDataObject, et que revenir serait un ennui, une copie inutile des données, et mettre en jeu deux copies du même objet.

Dans le reste du code, j'ai ignoré les sous-classes et utilisé les méthodes d'extension:

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

Et cela fonctionne bien.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top