有没有办法在 C# 的对象初始值设定项块中使用扩展方法

发布于 2024-07-07 05:19:08 字数 1967 浏览 11 评论 0原文

下面的简单演示捕获了我想要做的事情。 在实际程序中,我必须使用对象初始化块,因为它正在读取 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 查询中有效吗?

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.

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.

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.:

// 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

寂寞陪衬 2024-07-14 05:19:08

对象初始值设定项只是语法糖,需要聪明的编译器,并且从当前实现开始,您无法调用初始值设定项中的方法。

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

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.

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:

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

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

public class DataObjectWithBarValue : BaseDataObject
{
    public void BarValue
    {
        set
        {
            SetData("bar", value);
        }
        get
        {
            (int) GetData("bar");
        }
    }
}
墨落成白 2024-07-14 05:19:08

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

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

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

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

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.

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.

紙鸢 2024-07-14 05:19:08

更好的是:

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

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

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.

划一舟意中人 2024-07-14 05:19:08
 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);
 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);
一桥轻雨一伞开 2024-07-14 05:19:08

是否可以延长课程时间? 然后您可以轻松添加您需要的属性。

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

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.

金橙橙 2024-07-14 05:19:08

是的,从回答者那里了解到,“有没有办法在 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");
    }

而且它工作得很好。

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."

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

    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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文