使用C#中的默认构造函数初始化只读成员变量



我的代码中有一个简单的Factory实现。我的目标是让构建的对象保留对创建它们的工厂的引用,所以我把它们连接起来,类似于这样:

public class Factory {
    public T CreateObject<T>() where T : Foo, new() 
    {
        return new T() {
            parent = this
        };
    }
}
public class Foo {
    internal Factory parent;
    internal Foo() { }
}

这读起来很有效,但我一直在想,我可能想要parent变量,这样一旦工厂设置了它,就不能更改它。然而,如果我像internal readonly Factory parent;一样声明它,那么工厂就不能再在构造中设置它的值了。

我通常会通过提供一个带参数的构造函数来弥补这一点,但这会扼杀通用实现,因为AFAIKwhere T : new()意味着一个无参数的构造函数。

我可能只是缺少了一些C#技巧,但实现这样的东西的最佳方法是什么?(还是最好放弃readonly,并相信代码不会以不安全的方式修改parent---想到NullReferenceException---?)

我不认为你可以获得你想要的东西,但你可以创建一个只能赋值一次的属性,并让工厂覆盖它。但我想用户总是可以用new CreatingFactory强制覆盖,但我认为这会让它变得困难,至少可以清楚地表明你的意图。

你可以做下面这样的事情。

    class Foo
    {
        private Factory factory;
        public Factory CreatingFactory
        {
            get { return factory; }
            set
            {
                if (factory != null)
                {
                    throw new InvalidOperationException("the factory can only be set once");
                }
                factory = value;
            }
        }
    }
    class Factory
    {
        public T Create<T>()
            where T : Foo, new()
        {
            T t = new T()
            {
                CreatingFactory = this
            };
            return t;
        }
    }

创建后,我搜索并找到了一个可能比我更好的答案:有没有一种方法可以只在C#中设置一次属性

您可以使用反射来设置字段,这将在不考虑readonly修饰符的情况下工作:

public class Factory
{
    public T CreateObject<T>() where T : Foo, new()
    {
        T t = new T();
        t.GetType()
         .GetField("parent", BindingFlags.NonPublic | BindingFlags.Instance)
         .SetValue(t, this);
        return t;
    }
}

我不确定这是否正是您想要的,但我提出了一种方法,用Func替换您的泛型类型参数,该方法显示了如何构造Foo对象,并允许您在构造函数中设置父对象。

public class Factory {
    private Func<Factory, Foo> creator;
    public Factory(Func<Factory, Foo> creator) {
        this.creator = creator;
    }
    public Foo CreateObject()
    {
        return this.creator(this);
    }
}
public class Foo {
    internal readonly Factory parent;
    internal Foo(Factory parent) {
        this.parent = parent;
    }
}

然后

public void Main() {
    Factory myfactory = new Factory(fact => new Foo(fact));
    Foo myfoo = myfactory.CreateObject();
}

最新更新