¿Hay alguna forma de usar un método de extensión en un bloque de inicialización de objetos en C #

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

Pregunta

La simple demostración a continuación captura lo que estoy tratando de hacer. En el programa real, tengo que usar el bloque de inicialización de objetos ya que está leyendo una lista en una expresión de selección de LINQ to SQL, y hay un valor que quiero leer de la base de datos y almacenar en el objeto, pero el objeto no tiene una propiedad simple que pueda establecer para ese valor. En su lugar, tiene un almacén de datos XML.

Parece que no puedo llamar a un método de extensión en el bloque de inicialización de objetos, y que no puedo adjuntar una propiedad usando métodos de extensión.

Entonces, ¿estoy fuera de suerte con este enfoque? La única alternativa parece ser persuadir al propietario de la clase base para que la modifique para este escenario.

Tengo una solución existente donde subclaro BaseDataObject, pero esto también tiene problemas que no aparecen en este ejemplo simple. Los objetos se conservan y se restauran como BaseDataObject, los procesos y las pruebas se harían complejos.

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 de las respuestas (de mattlant) sugiere utilizar un método de extensión de estilo de interfaz fluida. por ejemplo:

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

¿Pero funcionará esto en una consulta LINQ?

¿Fue útil?

Solución

Aún mejor:

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

y puede usar este método de extensión para tipos derivados de BaseDataObject para encadenar métodos sin conversiones y preservar el tipo real cuando se deduce en un campo var o un tipo anónimo.

Otros consejos

Los inicializadores de objetos son solo azúcar sintáctica que requiere un compilador inteligente, y desde la implementación actual no se pueden llamar métodos en el inicializador.

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

Obtendrá el compilador para algo como esto:

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

La diferencia es que no puede haber problemas de sincronización. La variable x get ha asignado el BaseDataObject totalmente asignado de una vez, no puede alterar el objeto durante su inicialización.

Puede llamar al método de extensión después de la creación del objeto:

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

Puede cambiar SetBarValue para que sea una propiedad con get / set que se puede asignar durante la inicialización:

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

O, puedes subclasificar / usar el patrón de fachada para agregar el método a tu objeto:

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

No, pero puedes hacer esto ...:

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

Y haz que tu extensión devuelva el objeto como lo hace Linq Does.

EDITAR: este fue un buen pensamiento hasta que lo volví a leer y vi que la clase base fue desarrollada por una tercera persona: alias no tienes el código. Otros aquí han publicado una solución correcta.

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

¿Es una posibilidad extender la clase? Luego, puede agregar fácilmente la propiedad que necesita.

Si falla, puede crear una nueva clase que tenga propiedades similares que simplemente vuelvan a llamar a una instancia privada de la clase que le interesa.

Correcto, habiendo aprendido de los respondedores, la respuesta corta a " ¿Hay alguna forma de usar un método de extensión en un bloque de inicialización de objetos en C #? " es " No. "

La forma en que finalmente resolví el problema al que me enfrenté (similar, pero más complejo que el problema de los juguetes que planteé aquí) fue un enfoque híbrido, como sigue:

Creé una subclase, por ejemplo

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

Lo que funciona bien en LINQ, el bloque de inicialización se parece a

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

Pero la razón por la que no me gustó este enfoque en primer lugar es que estos objetos se colocan en XML y se vuelven a mostrar como BaseDataObject, y la devolución sería una molestia, una copia de datos innecesaria, y Pon dos copias del mismo objeto en juego.

En el resto del código, ignoré las subclases y usé los métodos de extensión:

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

Y funciona muy bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top