是否可以将 ac# 对象初始值设定项与工厂方法一起使用?

发布于 2024-07-15 02:33:12 字数 287 浏览 12 评论 0原文

我有一个带有静态工厂方法的类。 我想调用工厂来检索类的实例,然后进行额外的初始化,最好通过 C# 对象初始值设定项语法:

MyClass instance = MyClass.FactoryCreate()
{
  someProperty = someValue;
}

vs

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;

I have a class with a static factory method on it. I want to call the factory to retrieve an instance of the class, and then do additional initialization, preferablly via c# object initializer syntax :

MyClass instance = MyClass.FactoryCreate()
{
  someProperty = someValue;
}

vs

MyClass instance = MyClass.FactoryCreate();
instance.someProperty = someValue;

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

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

发布评论

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

评论(7

青丝拂面 2024-07-22 02:33:12

不。或者,您可以接受 lambda 作为参数,这也使您可以完全控制将调用“创建”过程的哪一部分。 通过这种方式,您可以这样调用它:

MyClass instance = MyClass.FactoryCreate(c=>
   {
       c.SomeProperty = something;
       c.AnotherProperty = somethingElse;
   });

创建看起来类似于:

public static MyClass FactoryCreate(Action<MyClass> initalizer)
{
    MyClass myClass = new MyClass();
    //do stuff
    initializer( myClass );
    //do more stuff
    return myClass;
}

另一种选择是返回一个构建器(使用 MyClass 的隐式强制转换运算符)。 您可以这样称呼:

MyClass instance = MyClass.FactoryCreate()
   .WithSomeProperty(something)
   .WithAnotherProperty(somethingElse);

检查构建器的 this

两者其中一些版本在编译时进行检查,并具有完整的智能感知支持。


第三个选项需要默认构造函数:

//used like:
var data = MyClass.FactoryCreate(() => new Data
{
    Desc = "something",
    Id = 1
});
//Implemented as:
public static MyClass FactoryCreate(Expression<Func<MyClass>> initializer)
{
    var myclass = new MyClass();
    ApplyInitializer(myclass, (MemberInitExpression)initializer.Body);
    return myclass ;
}
//using this:
static void ApplyInitializer(object instance, MemberInitExpression initalizer)
{
    foreach (var bind in initalizer.Bindings.Cast<MemberAssignment>())
    {
        var prop = (PropertyInfo)bind.Member;
        var value = ((ConstantExpression)bind.Expression).Value;
        prop.SetValue(instance, value, null);
    }
}

它介于编译时检查和不检查之间。 它确实需要一些工作,因为它强制在赋值上使用常量表达式。 我认为其他任何内容都是答案中已有方法的变体。 请记住,您也可以使用正常的分配,请考虑您是否确实需要其中任何一个。

No. Alternatively you could accept a lambda as an argument, which also gives you full control in which part of the "creation" process will be called. This way you can call it like:

MyClass instance = MyClass.FactoryCreate(c=>
   {
       c.SomeProperty = something;
       c.AnotherProperty = somethingElse;
   });

The create would look similar to:

public static MyClass FactoryCreate(Action<MyClass> initalizer)
{
    MyClass myClass = new MyClass();
    //do stuff
    initializer( myClass );
    //do more stuff
    return myClass;
}

Another option is to return a builder instead (with an implicit cast operator to MyClass). Which you would call like:

MyClass instance = MyClass.FactoryCreate()
   .WithSomeProperty(something)
   .WithAnotherProperty(somethingElse);

Check this for the builder

Both of these versions are checked at compile time and have full intellisense support.


A third option that requires a default constructor:

//used like:
var data = MyClass.FactoryCreate(() => new Data
{
    Desc = "something",
    Id = 1
});
//Implemented as:
public static MyClass FactoryCreate(Expression<Func<MyClass>> initializer)
{
    var myclass = new MyClass();
    ApplyInitializer(myclass, (MemberInitExpression)initializer.Body);
    return myclass ;
}
//using this:
static void ApplyInitializer(object instance, MemberInitExpression initalizer)
{
    foreach (var bind in initalizer.Bindings.Cast<MemberAssignment>())
    {
        var prop = (PropertyInfo)bind.Member;
        var value = ((ConstantExpression)bind.Expression).Value;
        prop.SetValue(instance, value, null);
    }
}

Its a middle between checked at compile time and not checked. It does need some work, as it is forcing constant expression on the assignments. I think that anything else are variations of the approaches already in the answers. Remember that you can also use the normal assignments, consider if you really need any of this.

明月松间行 2024-07-22 02:33:12

是的。 您可以通过以下技巧对已创建的实例使用对象初始值设定项。 您应该创建一个简单的对象包装器:

public struct ObjectIniter<TObject>
{
    public ObjectIniter(TObject obj)
    {
        Obj = obj;
    }

    public TObject Obj { get; }
}

现在您可以像这样使用它来初始化您的对象:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};

PS dotnet 存储库中的相关讨论:
https://github.com/dotnet/csharplang/issues/803

Yes. You can use object initializer for already created instance with the following trick. You should create a simple object wrapper:

public struct ObjectIniter<TObject>
{
    public ObjectIniter(TObject obj)
    {
        Obj = obj;
    }

    public TObject Obj { get; }
}

And now you can use it like this to initialize your objects:

new ObjectIniter<MyClass>(existingInstance)
{
    Obj =
    {
        //Object initializer of MyClass:
        Property1 = value1,
        Property2 = value2,
        //...
    }
};

P.S. Related discussion in dotnet repository:
https://github.com/dotnet/csharplang/issues/803

痞味浪人 2024-07-22 02:33:12

您可以使用如下扩展方法:

namespace Utility.Extensions
{
    public static class Generic
    {
        /// <summary>
        /// Initialize instance.
        /// </summary>
        public static T Initialize<T>(this T instance, Action<T> initializer)
        {
            initializer(instance);
            return instance;
        }
    }
}

您可以按如下方式调用它:

using Utility.Extensions;
// ...
var result = MyClass.FactoryCreate()
                .Initialize(x =>
                {
                    x.someProperty = someValue;
                    x.someProperty2 = someValue2;
                });

You can use an extension method such as the following:

namespace Utility.Extensions
{
    public static class Generic
    {
        /// <summary>
        /// Initialize instance.
        /// </summary>
        public static T Initialize<T>(this T instance, Action<T> initializer)
        {
            initializer(instance);
            return instance;
        }
    }
}

You would call it as follows:

using Utility.Extensions;
// ...
var result = MyClass.FactoryCreate()
                .Initialize(x =>
                {
                    x.someProperty = someValue;
                    x.someProperty2 = someValue2;
                });
乞讨 2024-07-22 02:33:12

“否”+1。

这是匿名对象方式的替代方法:

var instance = MyClass.FactoryCreate(
    SomeProperty => "Some value",
    OtherProperty => "Other value");

在这种情况下,FactoryCreate() 类似于:

public static MyClass FactoryCreate(params Func<object, object>[] initializers)
{
    var result = new MyClass();
    foreach (var init in initializers) 
    {
        var name = init.Method.GetParameters()[0].Name;
        var value = init(null);
        typeof(MyClass)
            .GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
            .SetValue(result, value, null);
    }
    return result;
}

+1 on "No".

Here's an alternative to the anonymous object way:

var instance = MyClass.FactoryCreate(
    SomeProperty => "Some value",
    OtherProperty => "Other value");

In this case FactoryCreate() would be something like:

public static MyClass FactoryCreate(params Func<object, object>[] initializers)
{
    var result = new MyClass();
    foreach (var init in initializers) 
    {
        var name = init.Method.GetParameters()[0].Name;
        var value = init(null);
        typeof(MyClass)
            .GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
            .SetValue(result, value, null);
    }
    return result;
}
錯遇了你 2024-07-22 02:33:12

不可以,对象初始值设定项只能在使用构造函数调用“new”时使用。 一种选择可能是向工厂方法添加一些额外的参数,以便在工厂内创建对象时设置这些值。

MyClass instance = MyClass.FactoryCreate(int someValue, string otherValue);

No, the object initializer can only be used on a call to "new" with the constructor. One option might be to add some additional args to your factory method, to set those values at object creation inside the factory.

MyClass instance = MyClass.FactoryCreate(int someValue, string otherValue);
忘东忘西忘不掉你 2024-07-22 02:33:12

正如大家所说,没有。

已经建议使用 lambda 作为参数。
更优雅的方法是接受匿名并根据对象设置属性。 即,

MyClass instance = MyClass.FactoryCreate(new {
    SomeProperty = someValue,
    OtherProperty = otherValue
});

这会慢得多,因为必须反映对象的所有属性。

Like everyone said, no.

A lambda as an argument has already been suggested.
A more elegant approach would be to accept an anonymous and set the properties according to the object. i.e.

MyClass instance = MyClass.FactoryCreate(new {
    SomeProperty = someValue,
    OtherProperty = otherValue
});

That would be much slower though, since the object would have to be reflected on for all the properties.

來不及說愛妳 2024-07-22 02:33:12

不,那是你只能“内联”做的事情。 工厂函数能为您做的就是返回一个引用。

No, that's something you can only do 'inline'. All the factory function can do for you is to return a reference.

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