有什么方法可以在C#的对象初始化程序块中使用扩展方法

时间:2020-03-06 14:42:38  来源:igfitidea点击:

下面的简单演示捕获了我正在尝试做的事情。在实际程序中,我必须使用对象初始化程序块,因为它正在读取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");
    }

而且效果很好。