7

下面的简单演示捕获了我正在尝试做的事情。在实际程序中,我必须使用对象初始化程序块,因为它正在读取 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)建议使用流畅的界面样式扩展方法。例如:

// 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 查询中工作吗?

4

6 回答 6

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");
        }
    }
}
于 2008-09-25T09:13:19.873 回答
4

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

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

并且让您的扩展程序像 Linq 一样返回对象。

编辑:这是一个好主意,直​​到我重新阅读并看到基类是由第三人开发的:也就是你没有代码。这里的其他人已经发布了正确的解决方案。

于 2008-09-25T09:16:54.077 回答
3

更好的是:

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

并且您可以对 BaseDataObject 的派生类型使用此扩展方法来链接方法而无需强制转换,并在推断为 var 字段或匿名类型时保留真实类型。

于 2008-09-25T09:33:52.427 回答
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);
于 2008-09-25T09:41:38.617 回答
0

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

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

于 2008-09-25T09:34:43.410 回答
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");
    }

而且效果很好。

于 2008-09-25T21:01:02.183 回答