Pergunta

A simples demonstração abaixo captura o que estou tentando fazer. No programa de verdade, eu tenho que usar o bloco objeto initialiser uma vez que está a ler uma lista em um LINQ to select expressão SQL, e não há um valor que que eu quero ler fora do banco de dados e armazenar no objeto, mas o objeto não tem uma propriedade simples que posso definir para esse valor. Em vez disso, tem um armazenamento de dados XML.

Parece que eu não posso chamar um método de extensão no bloco objeto initialiser, e que não pode anexar uma propriedade usando métodos de extensão.

Então estou fora de sorte com esta abordagem? A única alternativa parece ser a de convencer o dono da classe base para modificá-lo para este cenário.

Eu tenho uma solução existente onde eu subclasse BaseDataObject, mas isso não tem problemas, também, que não aparecem neste exemplo simples. Os objetos são persistentes e restaurado como BaseDataObject -. Os moldes e testes iria ficar complexo

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

Uma das respostas (a partir de mattlant) sugere a utilização de um método de extensão estilo de interface fluente. por exemplo:.

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

Mas este trabalho em uma consulta LINQ?

Foi útil?

Solução

Ainda melhor:

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

e você pode usar esse método de extensão para tipos derivados de BaseDataObject aos métodos cadeia sem moldes e preservar o tipo real quando inferida em um campo var ou tipo anônimo.

Outras dicas

Inicializadores de objeto são apenas açúcar sintático que requer um compilador inteligente, e como da implementação atual você não pode chamar métodos no inicializador.

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

Vai ter compilador para algo como isto:

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

A diferença é que não pode haver quaisquer problemas de sincronização. A variável x se de atribuído o BaseDataObject totalmente atribuído ao mesmo tempo, você não pode mexer com o objeto durante a sua inicialização.

Você poderia simplesmente chamar o método de extensão após a criação do objeto:

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

Você pode mudar SetBarValue ser uma propriedade com get / set que pode ser atribuído durante a inicialização:

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

Ou, você poderia subclasse / usar o padrão de fachada para adicionar o método para o seu objeto:

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

Não, mas você poderia fazer isso ....:

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

e ter sua extensão devolver o objeto como Linq faz.

EDIT: Este foi um bom pensado reler i untill e viu que a classe base foi desenvolvido por uma terceira pessoa: aka você não tem o código. Outros aqui postaram uma solução correta.

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

É estendendo a classe uma possibilidade? Então você pode facilmente adicionar o imóvel que você precisa.

Se isso falhar, você pode criar uma nova classe que tem propriedades semelhantes que simplesmente chamar de volta para uma instância particular da classe que você está interessado.

Right, tendo aprendido com os respondentes, a resposta mais curta para "Existe alguma maneira de usar um método de extensão em um objeto initializer bloco em C #?" é " Não. "

A maneira que eu finalmente resolveu o problema que eu enfrentei (semelhante, mas mais complexo que o problema brinquedo que eu levantei aqui) foi uma abordagem híbrida, como a seguir:

Eu criei uma subclasse, por exemplo.

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

O que funciona bem no LINQ, o bloco de inicialização parecendo

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

Mas a razão que eu não gostei dessa abordagem, em primeiro lugar é que esses objetos são colocados em XML e voltar como BaseDataObject, e lançando de volta ia ser um aborrecimento, copiar um conjunto de dados desnecessários, e seria colocar duas cópias do mesmo objeto em jogo.

No resto do código, eu ignorei as subclasses e métodos de extensão usados:

    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 ele funciona muito bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top