How do I set a readonly field in an initialize method that gets called from the constructor?

孤街浪徒 提交于 2020-12-29 04:58:38

问题


I'm sure I've seen somewhere that I can do the following by using an attribute above my Init() method, that tells the compiler that the Init() method must only be called from the constructor, thus allowing the readonly field to be set. I forgot what the attribute is called though, and I can't seem to find it on google.

public class Class
{
    private readonly int readonlyField;

    public Class()
    {
        Init();
    }

    // Attribute here that tells the compiler that this method must be called only from a constructor
    private void Init()
    {
        readonlyField = 1;
    }
}

回答1:


Rob's answer is the way to do it, in my book. If you need to initialize multiple fields you can do it using out parameters:

public class Class
{
    private readonly int readonlyField1;
    private readonly int readonlyField2;

    public Class()
    {
        Init(out readonlyField1, out readonlyField2);
    }

    protected virtual void Init(out int field1, out int field2)
    {
        field1 = 1;
        field2 = 2;
    }
}

Personally I find this makes sense in certain scenarios, such as when you want your fields to be readonly but you also want to be able to set them differently in a derived class (without having to chain a ton of parameters through some protected constructor). But maybe that's just me.




回答2:


Instead of using an Initialize method, how about inheriting a basic constructor through all your other constructors. i.e.

public class MyClass
{
    readonly int field1;
    readonly double field2;
    public MyClass(int field1, double field2)
    {
        //put whatever initialization logic you need here...
        field1 = 10;
        field2 = 30.2;
    }
    public MyClass(int field1, double field2p1, double field2p2)
        : this(field1, (field2p1 + field2p2))
    {
        //put anything extra in here
    }
}

This may be a little late to reach the original person in need, but it seems like this will cleanly solve the problem... Without the need to use any sort of nasty reflection or out parameters.




回答3:


The only solution I can think of is to return the value from the Init() method that the readonly field needs to be assigned:

public class Class
{
    private readonly int readonlyField;

    public Class()
    {
        readonlyField = Init();
    }

    private int Init()
    {
        return 1;
    }
}



回答4:


Jared is right; this is not possible. The workarounds I can think of are:

  1. Initialize the field in the declaration.
  2. Initialize the field in the constructor (Manually inline your Init method).
  3. Assign the field to a value returned by a method, e.g.: _myField = GetInitialMyFieldValue();
  4. Pass the field to the Init method, with the out modifier. This may be useful if you have many fields to initialize, which are dependent on constructor parameters. E.g.

 private readonly int _x;
 private readonly string _y;

 private void Init(int someConstructorParam, out int x, out string y){ .. }

 public Class(int someConstructorParam)
 {
     Init(someConstructorParam, out _x, out _y);
 } 



回答5:


This cannot be done. Fields which are tagged with readonly can only be set from the constructor




回答6:


What i ended up doing in current tech (C# 7.x) is use the value tuple system:

public class MyClass
{
    private readonly int x;
    private readonly int y;
    private readonly int z;

    public MyClass(int x)
    {
        this.x = x;
        (y, z) = InitYandZ();
    }

    private (int, int) InitYandZ()
    {
        return (5, 10);
    }
}

Not the cleanest either, But seems cleaner to me.




回答7:


C# compiler only allows you to set readonly fields if you're initializing them inline:

private readonly int readonlyField = 1;

or from the constructor:

public Class()
{
    readonlyField = 1;
}



回答8:


I think it works if use Reflection. Actually this works for me:

    public class Class
        {
            private readonly int readonlyField;
        public int MyField()
        {
             return readonlyField;
        }
        public Class()
        {
            readonlyField = 9;
        }
    }

and

static void Main(string[] args)
        {

            Class classObj = new Class();
            Console.WriteLine(classObj.MyField());//9

            Misc.SetVariableyByName(classObj, "readonlyField", 20);//20
            Console.WriteLine(classObj.MyField());
         }

this is SetVariableByName():

public static b

ool SetVariableyByName(object obj, string var_name, object value)
            {
                FieldInfo info = obj.GetType().GetField(var_name, BindingFlags.NonPublic| BindingFlags.Instance);
                if (info == null)
                return false;
            /* ELSE */
            info.SetValue(obj, value);
            return true;          
        }

the only thing is that readonlyField is public not private. I know that you can edit a private field, but am not sure why its not working for me!




回答9:


How about an initialized property with a getter only (as of C# 6.0)?

    private int MyProperty { get; } = 0;



回答10:


I know this is late, but what about using a class as a return value instead of using out params if there is a need to initialize multiple fields, are there any drawacks? Imho this is more convenient and more readable than using nested constructors.

public class MyInitClass
{
    public int Field1 { get; set; }
    public int Field2 { get; set; }
}

public class Class
{
    private readonly int readonlyField1;
    private readonly int readonlyField2;

    public Class()
    {
        var init = Init();
        readonlyField1 = init.Field1;
        readonlyField2 = init.Field2;
    }

    private MyInitClass Init()
    {
        return new MyInitClass() { Field1 = 1, Field2 = 2 };
    }
}


来源:https://stackoverflow.com/questions/3728447/how-do-i-set-a-readonly-field-in-an-initialize-method-that-gets-called-from-the

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!