有什么方法可以在C#的对象初始化程序块中使用扩展方法
下面的简单演示捕获了我正在尝试做的事情。在实际程序中,我必须使用对象初始化程序块,因为它正在读取LINQ to SQL select表达式中的列表,并且有一个值我想从数据库中读取并存储在对象上,但是该对象没有可以为该值设置的简单属性。相反,它具有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)建议使用流畅的界面样式扩展方法。例如。:
// 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查询中工作吗?
解决方案
对象初始化程序只是需要聪明编译器的语法糖,从当前的实现开始,我们不能在初始化程序中调用方法。
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 } }
或者,我们可以继承/使用facade模式将方法添加到对象上:
public class DataObjectWithBarValue : BaseDataObject { public void BarValue { set { SetData("bar", value); } get { (int) GetData("bar"); } } }
不,但是我们可以这样做。...:
BaseDataObject test2 = (new BaseDataObject { SomeValue = 3, SomeOtherValue = 4}).SetBarValue(5);
并让扩展名像Linq所做的那样返回对象。
编辑:这是一个好主意,直到我重读并看到基类是由第三人开发的:又名我们没有代码。其他人在这里发布了正确的解决方案。
更好的是:
public static T SetBarValue<T>(this T dataObject, int barValue) where T : BaseDataObject { dataObject.SetData("bar", barValue); return dataObject; }
并且我们可以将此扩展方法用于BaseDataObject的派生类型,以在不强制转换的情况下链接方法,并在推断为var字段或者匿名类型时保留实型。
上课有可能吗?然后,我们可以轻松添加所需的属性。
失败的话,我们可以创建一个具有类似属性的新类,该类只需调用我们感兴趣的类的私有实例。
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);
是的,从回答者那里学到的简短答案是"在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"); }
而且效果很好。