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

时间:2022-09-02 09:29:11

The simple demo below captures what I am trying to do. In the real program, I have to use the object initialiser block since it is reading a list in a LINQ to SQL select expression, and there is a value that that I want to read off the database and store on the object, but the object doesn't have a simple property that I can set for that value. Instead it has an XML data store.

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

It looks like I can't call an extension method in the object initialiser block, and that I can't attach a property using extension methods.

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

So am I out of luck with this approach? The only alternative seems to be to persuade the owner of the base class to modify it for this scenario.

我对这种方法运气不好吗?唯一的替代方案似乎是说服基类的所有者为这种情况修改它。

I have an existing solution where I subclass BaseDataObject, but this has problems too that don't show up in this simple example. The objects are persisted and restored as BaseDataObject - the casts and tests would get complex.

我有一个现有的解决方案,我将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) };
    }
}

One of the answers (from mattlant) suggests using a fluent interface style extension method. e.g.:

其中一个答案(来自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);

But will this work in a LINQ query?

但这会在LINQ查询中工作吗?

6 个解决方案

#1


3  

Even better:

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

and you can use this extension method for derived types of BaseDataObject to chain methods without casts and preserve the real type when inferred into a var field or anonymous type.

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

#2


4  

No but you could do this....:

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

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

ANd have your extension return the object like Linq Does.

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

EDIT: This was a good thought untill i reread and saw that the base class was developed by a third person: aka you dont have the code. Others here have posted a correct solution.

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

#3


4  

Object Initializers are just syntactic sugar that requires a clever compiler, and as of the current implementation you can't call methods in the initializer.

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

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

Will get compiler to something like this:

会得到这样的编译器:

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

The difference is that there can't be any synchronization issues. The variable x get's assigned the fully assigned BaseDataObject at once, you can't mess with the object during it's initialization.

不同之处在于不存在任何同步问题。变量x get一次分配了完全赋值的BaseDataObject,在初始化期间不能弄乱对象。

You could just call the extension method after the object creation:

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

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

You could change SetBarValue to be a property with get/set that can be assigned during initialization:

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

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

Or, you could subclass / use the facade pattern to add the method onto your object:

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

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

#4


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

#5


0  

Is extending the class a possibility? Then you could easily add the property you need.

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

Failing that, you can create a new class that has similar properties that simply call back to a private instance of the class you are interested in.

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

#6


0  

Right, having learned from the answerers, the short answer to "Is there any way to use an extension method in an object initializer block in C#?" is "No."

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

The way that I eventually solved the problem that I faced (similar, but more complex that the toy problem that I posed here) was a hybrid approach, as follows:

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

I created a subclass, e.g.

我创建了一个子类,例如

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

Which works fine in LINQ, the initialisation block looking like

这在LINQ中运行良好,初始化块看起来像

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

But the reason that I didn't like this approach in the first place is that these objects are put into XML and come back out as BaseDataObject, and casting back was going to be an annoyance, an unnecessary data copy, and would put two copies of the same object in play.

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

In the rest of the code, I ignored the subclasses and used extension methods:

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

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

And it works nicely.

而且效果很好。

#1


3  

Even better:

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

and you can use this extension method for derived types of BaseDataObject to chain methods without casts and preserve the real type when inferred into a var field or anonymous type.

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

#2


4  

No but you could do this....:

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

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

ANd have your extension return the object like Linq Does.

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

EDIT: This was a good thought untill i reread and saw that the base class was developed by a third person: aka you dont have the code. Others here have posted a correct solution.

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

#3


4  

Object Initializers are just syntactic sugar that requires a clever compiler, and as of the current implementation you can't call methods in the initializer.

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

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

Will get compiler to something like this:

会得到这样的编译器:

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

The difference is that there can't be any synchronization issues. The variable x get's assigned the fully assigned BaseDataObject at once, you can't mess with the object during it's initialization.

不同之处在于不存在任何同步问题。变量x get一次分配了完全赋值的BaseDataObject,在初始化期间不能弄乱对象。

You could just call the extension method after the object creation:

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

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

You could change SetBarValue to be a property with get/set that can be assigned during initialization:

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

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

Or, you could subclass / use the facade pattern to add the method onto your object:

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

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

#4


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

#5


0  

Is extending the class a possibility? Then you could easily add the property you need.

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

Failing that, you can create a new class that has similar properties that simply call back to a private instance of the class you are interested in.

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

#6


0  

Right, having learned from the answerers, the short answer to "Is there any way to use an extension method in an object initializer block in C#?" is "No."

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

The way that I eventually solved the problem that I faced (similar, but more complex that the toy problem that I posed here) was a hybrid approach, as follows:

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

I created a subclass, e.g.

我创建了一个子类,例如

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

Which works fine in LINQ, the initialisation block looking like

这在LINQ中运行良好,初始化块看起来像

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

But the reason that I didn't like this approach in the first place is that these objects are put into XML and come back out as BaseDataObject, and casting back was going to be an annoyance, an unnecessary data copy, and would put two copies of the same object in play.

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

In the rest of the code, I ignored the subclasses and used extension methods:

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

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

And it works nicely.

而且效果很好。