Can you detect if a C# field has been assigned a d

2019-03-27 10:28发布

Say you have a class declaration, e.g.:


class MyClass
{
  int myInt=7;
  int myOtherInt;
}

Now, is there a way in generic code, using reflection (or any other means, for that matter), that I can deduce that myInt has a default value assigned, whereas myOtherInt does not? Note the difference between being initialised with an explicit default value, and being left to it's implicit default value (myOtherInt will be initialised to 0, by default).

From my own research it looks like there is no way to do this - but I thought I'd ask here before giving up.

[Edit]

Even with nullable and reference types I want to distingush between those that have been left as null, and those that have been explicitly initialised to null. This is so that I can say that fields with an initialiser are "optional" and other fields are "mandatory". At the moment I'm having to do this using attributes - which niggles me with their redundancy of information in this case.

12条回答
▲ chillily
2楼-- · 2019-03-27 10:39

I compiled your code and load it up in ILDASM and got this

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
    // Code size       15 (0xf)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldc.i4.7
    IL_0002:  stfld      int32 dummyCSharp.MyClass::myInt
    IL_0007:  ldarg.0
    IL_0008:  call       instance void [mscorlib]System.Object::.ctor()
    IL_000d:  nop
    IL_000e:  ret
} // end of method MyClass::.ctor

Note the ldc.i4.7 and stfld int32 dummyCSharp.MyClass::myInt seems to be instructions to set the default values for the myInt field.

So such assignment is actually compiled as an additional assignment statement in a constructor.

To detect such assignment, then you will need reflection to reflect on the IL of MyClass's constructor method and look for stfld (set fields?) commands.


EDIT: If I add some assignment into the constructor explicitly:

class MyClass
{
    public int myInt = 7;
    public int myOtherInt;

    public MyClass()
    {
        myOtherInt = 8;
    }
}

When I load it up in ILDASM, I got this:

.method public hidebysig specialname rtspecialname 
                instance void  .ctor() cil managed
{
    // Code size       24 (0x18)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldc.i4.7
    IL_0002:  stfld      int32 dummyCSharp.MyClass::myInt
    IL_0007:  ldarg.0
    IL_0008:  call       instance void [mscorlib]System.Object::.ctor()
    IL_000d:  nop
    IL_000e:  nop
    IL_000f:  ldarg.0
    IL_0010:  ldc.i4.8
    IL_0011:  stfld      int32 dummyCSharp.MyClass::myOtherInt
    IL_0016:  nop
    IL_0017:  ret
} // end of method MyClass::.ctor

Note that the extra assigment on myOtherInt that I added was affffded after a call the Object class's constructor.

IL_0008:  call       instance void [mscorlib]System.Object::.ctor()

So there you have it,

Any assignment done before the call to Object class's constructor in IL is a default value assignment.

Anything following it is a statement inside the class's actual constructor code.

More extensive test should be done though.

p.s. that was fun :-)

查看更多
倾城 Initia
3楼-- · 2019-03-27 10:39

For value types using a nullable type for optional parameters should work. Strings could also be initialised to empty if they are not optional.

int mandatoryInt;
int? optionalInt;

However this does strike me as a bit dirty, I would stick with attributes as a clear way of doing this.

查看更多
smile是对你的礼貌
4楼-- · 2019-03-27 10:41

What about making a generic struct that contains a value and an initialized flag?

public struct InitializationKnown<T> {
    private T m_value;
    private bool m_initialized;

    // the default constructor leaves m_initialized = false, m_value = default(T)
    // InitializationKnown() {}

    InitializationKnown(T value) : m_value(value), m_initialized(true) {}

    public bool initialized { 
        get { return m_initialized; }
    }
    public static operator T (InitializationKnown that) {
        return that.m_value;
    }
    // ... other operators including assignment go here
}

Then just use this in place of the members you need to know about the initialization of. Its a pretty basic variation on a lazy future or promise.

查看更多
smile是对你的礼貌
5楼-- · 2019-03-27 10:48

The compiler can be set to generate a warning if you try to use a variable before assigning it a value. I have the default setting and that how it behave.

查看更多
The star\"
6楼-- · 2019-03-27 10:52

You might want to consider a nullable int for this behavior:

class MyClass
{
  int? myInt = 7;
  int? myOtherInt = null;
}
查看更多
时光不老,我们不散
7楼-- · 2019-03-27 10:55

You could wrap the fields in private/protected properties. If you want to know if its been set or not, check the private field (e.g. _myInt.HasValue()).

class MyClass
{

    public MyClass()
    {
        myInt = 7;
    }

    int? _myInt;
    protected int myInt
    {
        set { _myInt = value; }
        get { return _myInt ?? 0; }
    }

    int? _myOtherInt;
    protected int myOtherInt
    {
        set { _myOtherInt = value; }
        get { return _myOtherInt ?? 0; }
    }
}
查看更多
登录 后发表回答