2

I am wondering how to get the default value of a field of any arbitrary class.

For example,

public class Example
{
    public int a = 10;
}

I want to get the value 10 when only typeof(Example) is given.

This default value is useful because I am writing a function to create a non-null default instance for any type. It would be nice to have the instance initialized to the values given in the declaration.

Please note that creating an instance through a default constructor and grab the value is not an option to me because I hope the function I am working on can be applied to classes with or without default constructors.

Also, DefaultValueAttribute is not an option for me either. As far as I know, attributes can only take several types as its argument. So I cannot get the default value of fields of self-defined types.

Edit 1:

I know this question seems to be a duplication of Get default value of class member, but it is not. In the referenced question, the OP is satisfied with using constructor to get a real instance of the class. But in my case, I cannot be sure that a default constructor exists for the class I am dealing with.

I am writing a default value generator because I need a fake object of an arbitrary type. That is, there is no assumption made of how the type is going to be. The fake object will be inspected through a GUI, and a user can edit the values of fields that are exposed in the inspector. Here the arbitrary type is important to me because the inspector should work on not only types defined by me but also on types defined in libraries. Moreover, the fake object should never be null or the inspector will blow up, LOL.

FormatterServices.GetSafeUninitializedObject gets me an acceptable fake object as it allocates memory blocks and initialize them to zeros. But I feel like it would be better if the fake object can be further initialized so the fields can have their default values shown in the inspector.

Thanks to Patrick Hofman reminds me that the "default value" I am talking about is not the real default but a expression that will be evaluated in constructors. That being said, I am still wondering if it is possible to fetch the field initialization expression and event evaluate it.

I think there should be a way of doing it because decompilers such as ILSpy can shows the field initializer in the decompiled results.

Community
  • 1
  • 1
YAC
  • 405
  • 4
  • 14
  • 3
    What if the type's constructor assigns another value? Then your "default" value is pointless. – Tim Schmelter Sep 28 '15 at 15:13
  • Can you just use (new Example()).a, can't you? – enkryptor Sep 28 '15 at 15:15
  • 2
    There is no sane way of doing this without creating an instance of the type or relying on metadata like an attribute. – vcsjones Sep 28 '15 at 15:17
  • 1
    There is no way to get this value without initializing this type like in the duplicated question since the compilation process inserts this value assignment to the constructor. – Tamir Vered Sep 28 '15 at 15:18
  • Create an interface that has a `Value` property, have your class implement the interface. Then use the Activaor to creat an instance as the interface, and call the property getter. something like: `interface IHasClassValue { TValueType DefaultValue { get; } }` and `class Test : IHasClassValue { int IHasClassValue.DefaultValue { get { return 10; } } }` then finally `var val = (Activator.CreateInstance(typeof(Test)) as IHasClassValue).DefaultValue;` – asawyer Sep 28 '15 at 15:18
  • 1
    Purely out of curiosity I ask this - but why? Why do you want to do this? What is the requirement? (Genuinely interested). –  Sep 28 '15 at 15:19
  • I'm going to re-open this since I think it is different enough to not be an dupe - this question has constraints and needs that are different from the duplicate (regardless of how feasible the constraints are). – vcsjones Sep 28 '15 at 15:20
  • Honestly though I'd setup a seperate type/value dictionary to map it. Something like `IDictionary DefaultValuesForTypes` `DefaultValuesForTypes.Add(Typeof(Test), 10);` then get the value with `var val = DefaultValueForTypes[typeof(Test)]` – asawyer Sep 28 '15 at 15:20
  • "It would be nice to have the instance initialized to the values given in the declaration" - this happens by default. However, as @TimSchmelter mentioned, a constructor can assign a different value. – user1620220 Sep 28 '15 at 15:35
  • I wanted to answer with [`FormatterServices.GetUninitializedObject`](https://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatterservices.getuninitializedobject.aspx) but that doesn't initialise the fields. "*Because the new instance of the object is initialized to zero and no constructors are run, the object might not represent a state that is regarded as valid by that object. The current method should only be used for deserialization when the user intends to immediately populate all fields.*" – Wai Ha Lee Sep 28 '15 at 15:57
  • ... You might find something on "[*Create object instance without invoking constructor?*](http://stackoverflow.com/q/296584/1364007)" useful. – Wai Ha Lee Sep 28 '15 at 15:59
  • Actually I have studied those two stack overflow articles already. I asked this question because I am not satisfied with the solutions provided in these articles. – YAC Sep 28 '15 at 16:18
  • create readonly static field that holds default values. – M.kazem Akhgary Sep 28 '15 at 16:34

2 Answers2

4

You can only get values of constants, not any other type of variables, since they need to have an initialized class to retrieve a value from, which aren't real defaults any more, just the current value.

The previously duplicate Get default value of class member tells what to do if you drop the don't create an instance constraint.

What about this one?

public class Example
{
    public readonly DateTime a = DateTime.Now;
}

Can you tell me what the default value of this field is? Even if you could, it isn't really the default value, it is the evaluated value of a certain variable in a point-in-time.

Community
  • 1
  • 1
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
2

You can inspect the IL code, and look for members called .ctor (these are the instance constructors).

Then find one which does not call another Example::.ctor overload, but calls instead a base-class constructor, in your case [mscorlib]System.Object::.ctor(). (If there does not exist one, the instance field initializers are unreachable (pathological case)!)

Then, inside the IL of this member, the field assignments stfld that come before the call to the base class .ctor, are the field initializers. Voilà!

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181