views:

124

answers:

7

Hi

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;
    }
}
+7  A: 

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

JaredPar
A: 

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;
}
Steve Michelotti
+3  A: 

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;
    }
}
Rob
I was sure I saw this done.. My brain must be playing tricks on me. Thanks all
Ben Anderson
@BenAnderson, I know what you mean - I spent hours a few days ago trawling the 'net as I was sure that I'd read somewhere that SMB2 allows a file-copy from one remote server to another to not be routed via the client machine making the request. Hours lost and I could find no evidence, though I was *adamant* I'd read that it was possible! :S
Rob
+4  A: 

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.

Dan Tao
out parameters, yuk!
Chuck Conway
@Chuck: Hey, I never said it was pretty.
Dan Tao
I solved it using Reflection. See below..
Derar
@Derar: Damn, dude... I think reflection is definitely *not* the way to go about this. Why use reflection on your own class, when all the code is right in front of you?
Dan Tao
@Dan, I down voted your solution by mistake. I was just under the impression that everyone says it's not doable. This is why I added that reflection-based solution. Good solution dude!
Derar
+5  A: 

Nope, you can't. Basically you should perform initialization in the constructor - that's what it's there for.

For reuse purposes, if you have multiple constructors, try to make all but one of them chain to a "master" constructor which does all the real work.

If any of the values requires complex calculation, then it may not be appropriate to be calculated in the constructor anyway - and if it is, you can put the calculation part into a private method which returns the value, then call that method from the constructor.

EDIT: I wouldn't include reflection as a real way of doing this. There are all kinds of statements you can make which don't include abuses via reflection. Want the string literal "x" to turn into "y"? Sure, you can do that with reflection, if you have the right permissions... but you absolutely shouldn't.

Jon Skeet
can be solved via Reflection. See below the example.
Derar
+2  A: 

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);
 } 
Ani
A: 

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!

Derar
works now on Private member variables..
Derar
Just because you *can* do it this way doesn't mean that you *should*... and of course in many situations you won't have permission to do this.
Jon Skeet
I don't think this is what reflection was meant for. Unnecessary performance hit...
clyc
Well guys, true! But if you weren't given the choice, and have to deal with such a situation, you have to do something.. Reflection is not the best technique to use, but it solves problems.
Derar