下面的简单演示捕获了我想要做的事情。在真正的程序中,我必须使用对象初始化块,因为它正在读取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查询中起作用吗?
答案 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");
}
它很好用。