有没有办法在C#中的对象初始化程序块中使用扩展方法

时间:2008-09-25 09:02:52

标签: c# linq extension-methods initializer

下面的简单演示捕获了我想要做的事情。在真正的程序中,我必须使用对象初始化块,因为它正在读取LINQ to SQL选择表达式中的列表,并且有一个值我要读取数据库并存储在对象上,但是对象没有我可以为该值设置的简单属性。相反,它有一个XML数据存储。

看起来我无法在对象初始化程序块中调用扩展方法,并且我无法使用扩展方法附加属性。

我对这种方法感到不快吗?唯一的选择似乎是说服基类的所有者为这种情况修改它。

我有一个现有的解决方案,我将BaseDataObject子类化,但是这个问题也没有出现在这个简单的例子中。对象被持久化并恢复为BaseDataObject - 转换和测试将变得复杂。

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

其中一个答案(来自mattlant)建议使用流畅的界面风格扩展方法。 e.g:

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

但这会在LINQ查询中起作用吗?

6 个答案:

答案 0 :(得分:5)

对象初始化器只是语法糖,需要一个聪明的编译器,并且从当前实现开始,你不能在初始化器中调用方法。

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

将编译器变为这样的东西:

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

不同之处在于不存在任何同步问题。变量x get一次分配了完全赋值的BaseDataObject,在初始化过程中你不能弄乱它。

您可以在创建对象后调用扩展方法:

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

您可以将SetBarValue更改为可在初始化期间分配的get / set属性:

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

或者,您可以继承/使用外观模式将方法添加到对象上:

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

答案 1 :(得分:4)

不,但你可以这样做......:

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

你的扩展程序返回像Linq那样的对象。

编辑:这是一个很好的想法,直到我重读并看到基类是由第三人开发的:又名你没有代码。其他人已经发布了正确的解决方案。

答案 2 :(得分:3)

更好:

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

并且您可以将此扩展方法用于派生类型的BaseDataObject,以链接没有强制转换的方法,并在推断为var字段或匿名类型时保留实际类型。

答案 3 :(得分: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);

答案 4 :(得分:0)

是否有可能扩展课程?然后,您可以轻松添加所需的属性。

如果不这样做,您可以创建一个具有类似属性的新类,只需回调您感兴趣的类的私有实例。

答案 5 :(得分:0)

是的,从答复者那里学到了很简单的回答:“有没有办法在C#中的对象初始化程序块中使用扩展方法?”是“否。

我最终解决了我所面临的问题(类似,但我在这里提出的玩具问题更为复杂)是一种混合方式,如下所示:

我创建了一个子类,例如

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

在LINQ中可以正常工作,初始化块看起来像

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

但是我首先不喜欢这种方法的原因是这些对象被放入XML并作为BaseDataObject返回,并且返回将是一个烦恼,一个不必要的数据副本,并且会将两个相同对象的副本放在游戏中。

在其余的代码中,我忽略了子类并使用了扩展方法:

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

它很好用。