Frage

Sagen Sie bitte eine Klassendeklaration haben, z.


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

, ist es eine Möglichkeit, in generischem Code, mithilfe von Reflektion (oder anderen Mitteln, was das betrifft), dass ich ableiten kann, dass myInt einen Standardwert hat zugewiesen, während myOtherInt nicht? Beachten Sie den Unterschied zwischen mit einem expliziten Standardwert initialisiert wird, und gelassen zu werden, um es impliziter Standardwert ist (myOtherInt wird auf 0 initialisiert werden, Standardeinstellung).

Aus meiner eigenen Forschung sieht es aus wie es keine Möglichkeit, dies zu tun ist -. Aber ich dachte, dass ich, bevor er aufgibt hier fragen würde

[Bearbeiten]

Auch bei nullable und Referenztypen ich zwischen denen, distingush wollen, die als null gestellt worden sind, und diejenigen, die explizit initialisiert auf null wurden. Dies ist so, dass ich sagen kann, dass Felder mit einem initialiser sind „optional“ und andere Bereiche sind „Pflicht“. Im Moment habe ich dies mit Attributen zu tun -., Die mich mit ihrer Redundanz von Informationen, die in diesem Fall Zipperlein

War es hilfreich?

Lösung

ich Ihren Code kompiliert und laden Sie es in ILDASM und bekam diese

.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

Beachten Sie die ldc.i4.7 und stfld int32 dummyCSharp.MyClass::myInt scheint Anweisungen zu sein, die Standardwerte für das myInt Feld zu setzen.

So eine solche Zuordnung wird tatsächlich als zusätzliche Zuweisungsanweisung in einem Konstruktor erstellt.

solche Zuordnung zu erkennen, dann werden Sie Reflexion müssen auf die IL von MyClass Konstruktor-Methode zu reflektieren und sucht stfld (Set-Felder?) Befehle.


EDIT: Wenn ich eine Zuordnung in den Konstruktor hinzufügen explizit:

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

    public MyClass()
    {
        myOtherInt = 8;
    }
}

Wenn ich es in ILDASM laden, ich habe dies:

.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

Beachten Sie, dass die zusätzliche assigment auf myOtherInt, die ich hinzugefügt wurde added nach ein Aufruf der Konstruktor der Object-Klasse.

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

So haben Sie es,

Die Abtretung erfolgt vor der Anruf Klasse Konstruktor in IL-Objekt ist ein Standardwert Zuordnung.

Alles, was folgt es eine Anweisung innerhalb der tatsächlichen Konstruktorcode Klasse ist.

Weitere umfangreicher Test obwohl getan werden sollte.

P. S. das hat Spaß gemacht: -)

Andere Tipps

Sie möchten vielleicht ein Nullable-int für dieses Verhalten berücksichtigen:

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

Ein Standardwert ist ein Wert, wie jedes andere. Es gibt keine Möglichkeit, zwischen diesen beiden Fällen zu unterscheiden:

int explicitly = 0;
int implicitly;

In beiden Fällen geben Sie ihnen den Wert 0, eine Möglichkeit, Sie mit der Eingabe nur spart. Es gibt keine magische „default nicht initialisierten Wert“ - sie sind beide Null. Sie arbeiten genau gleich sein werden. Jedoch, dass Sie die Tatsache, erwägen auch dies zeigt an, dass Sie ernsthaft von der Strecke guter Ideen sind. Was machen Sie? Was ist Ihre spezifischen Bedürfnisse? Sie haben die falsche Frage;)

Hier ist, was ich tun würde, wenn ich dies als eine allgemeine Laufzeit-Funktion bauen will. Für skalare Typen, würde ich einen Standardwert Attribut erstellen und dass defaulticity zu bestimmen, verwendet werden.

Hier ist eine Teillösung für die Aufgabe - ich bin sicher, es könnte besser sein, aber ich klopfe es einfach aus:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Linq;
using System.Data;


namespace FieldAttribute
{
    [global::System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
    sealed class DefaultValueAttribute : Attribute
    {
        public DefaultValueAttribute(int i)
        {
            IntVal = i;
        }

        public DefaultValueAttribute(bool b)
        {
            BoolVal = b;
        }

        public int IntVal { get; set; }
        public bool BoolVal { get; set; }

        private static FieldInfo[] GetAttributedFields(object o, string matchName)
        {
            Type t = o.GetType();
            FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            return fields.Where(fi => ((matchName != null && fi.Name == matchName) || matchName == null) &&
                            (fi.GetCustomAttributes(false).Where(attr => attr is DefaultValueAttribute)).Count() > 0).ToArray();
        }

        public static void SetDefaultFieldValues(object o)
        {
            FieldInfo[] fields = GetAttributedFields(o, null);
            foreach (FieldInfo fi in fields)
            {
                IEnumerable<object> attrs = fi.GetCustomAttributes(false).Where(attr => attr is DefaultValueAttribute);
                foreach (Attribute attr in attrs)
                {
                    DefaultValueAttribute def = attr as DefaultValueAttribute;
                    Type fieldType = fi.FieldType;
                    if (fieldType == typeof(Boolean))
                    {
                        fi.SetValue(o, def.BoolVal);
                    }
                    if (fieldType == typeof(Int32))
                    {
                        fi.SetValue(o, def.IntVal);
                    }
                }
            }
        }

        public static bool HasDefaultValue(object o, string fieldName)
        {
            FieldInfo[] fields = GetAttributedFields(o, null);
            foreach (FieldInfo fi in fields)
            {
                IEnumerable<object> attrs = fi.GetCustomAttributes(false).Where(attr => attr is DefaultValueAttribute);
                foreach (Attribute attr in attrs)
                {
                    DefaultValueAttribute def = attr as DefaultValueAttribute;
                    Type fieldType = fi.FieldType;
                    if (fieldType == typeof(Boolean))
                    {
                        return (Boolean)fi.GetValue(o) == def.BoolVal;
                    }
                    if (fieldType == typeof(Int32))
                    {
                        return (Int32)fi.GetValue(o) == def.IntVal;
                    }
                }
            }
            return false;
        }
    }

    class Program
    {
        [DefaultValue(3)]
        int foo;

        [DefaultValue(true)]
        bool b;

        public Program()
        {
            DefaultValueAttribute.SetDefaultFieldValues(this);
            Console.WriteLine(b + " " + foo);
            Console.WriteLine("b has default value? " + DefaultValueAttribute.HasDefaultValue(this, "b"));
            foo = 2;
            Console.WriteLine("foo has default value? " + DefaultValueAttribute.HasDefaultValue(this, "foo"));
        }

        static void Main(string[] args)
        {
            Program p = new Program();
        }
    }
}

Für Werttypen ein Nullable Type mit für optionale Parameter funktionieren sollte. Strings könnte auch leeren initialisiert werden, wenn sie nicht sind optional.

int mandatoryInt;
int? optionalInt;

Doch dieser erstaunt ich als ein bisschen schmutzig, ich mit Attributen als klare Art und Weise halten würde, dies zu tun.

Kann sein, dies ist nicht die einfachste Lösung ...

Sie können mit de Default Attribut den Wert einzustellen wie:

Import System.ComponentModel und System.Reflection

private int myNumber = 3;
[System.ComponentModel.DefaultValue(3)]
public int MyNumber
{
    get
    {
        return myNumber;
    }
    set
    {
        myNumber = value;
    }
}

Und dann wiederherstellen den Standardwert mit Reflexion:

PropertyInfo prop = this.GetType().GetProperty("MyNumber");
MessageBox.Show(((DefaultValueAttribute)(prop.GetCustomAttributes(typeof(DefaultValueAttribute), true).GetValue(0))).Value.ToString());

Was ist eine generische Struktur zu machen, der einen Wert enthält und einen initialisierten 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
}

Dann ist diese nur verwenden anstelle der Mitglieder über die Initialisierung von Ihnen wissen müssen. Es ist eine ziemlich grundlegende Veränderung auf einer faulen Zukunft oder Versprechen.

Dieser Ansatz nutzt die Eigenschaft get / set-Prozess:

    class myClass
    {
       #region Property: MyInt
       private int _myIntDefault = 7;
       private bool _myIntChanged = false;
       private int _myInt;
       private int MyInt
       {
          get
          {
             if (_myIntChanged)
             {
                return _myInt;
             }
             else
             {
                return _myIntDefault;
             }
          }
          set
          {
             _myInt = value;
             _myIntChanged = true;
          }
       }

       private bool MyIntIsDefault
       {
          get
          {
             if (_myIntChanged)
             {
                return (_myInt == _myIntDefault);
             }
             else
             {
                return true;
             }
          }
       }
       #endregion
    }

Das ist eine Menge Code für ein Feld - hallo Schnipsel

!

Sie können die Felder in privaten / geschützten Eigenschaften wickeln. Wenn Sie wissen wollen, ob seine gesetzt worden ist oder nicht, überprüfen Sie den privaten Bereich (z _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; }
    }
}

Wenn das, was Sie wollen, ist dies, dann am unteren Rand den Code überprüfen.
Es ist in Oxygene [1] geschrieben, hoffe, das ist kein Problem.

[1] oder Delphi Prism, wie es jetzt genannt


var inst1 := new Sample();
var inst2 := new Sample(X := 2);

var test1 := new DefaultValueInspector<Sample>(true);
var test2 := new DefaultValueInspector<Sample>(inst2, true);

var d := test1.DefaultValueByName["X"];

var inst1HasDefault := test1.HasDefaultValue(inst1, "X");
var inst2HasDefault := test1.HasDefaultValue(inst2, "X");

Console.WriteLine("Value: {0}; inst1HasDefault: {1}; inst2HasDefault {2}",
                  d, inst1HasDefault, inst2HasDefault);

d := test2.DefaultValueByName["X"];

inst1HasDefault := test2.HasDefaultValue(inst1, "X");
inst2HasDefault := test2.HasDefaultValue(inst2, "X");

Console.WriteLine("Value: {0}; inst1HasDefault: {1}; inst2HasDefault {2}",
                  d, inst1HasDefault, inst2HasDefault);

Ausgabe:

Value: 1; inst1HasDefault: True; inst2HasDefault False
Value: 2; inst1HasDefault: False; inst2HasDefault True


uses 
    System.Collections.Generic, 
    System.Reflection;

type
    DefaultValueInspector<T> = public class
    private
        method get_DefaultValueByName(memberName : String): Object;
        method get_DefaultValueByMember(memberInfo : MemberInfo) : Object;
   protected
        class method GetMemberErrorMessage(memberName : String) : String;
        method GetMember(memberName : String) : MemberInfo;

        property MembersByName : Dictionary<String, MemberInfo> 
            := new Dictionary<String, MemberInfo>(); readonly;

        property GettersByMember : Dictionary<MemberInfo, Converter<T, Object>> 
            := new Dictionary<MemberInfo, Converter<T, Object>>(); readonly;

        property DefaultValuesByMember : Dictionary<MemberInfo, Object> 
            := new Dictionary<MemberInfo, Object>(); readonly;
    public
        property UseHiddenMembers : Boolean; readonly;

        property DefaultValueByName[memberName : String] : Object
            read get_DefaultValueByName;
        property DefaultValueByMember[memberInfo : MemberInfo] : Object
            read get_DefaultValueByMember;

        method GetGetMethod(memberName : String) : Converter<T, Object>;
        method GetGetMethod(memberInfo : MemberInfo) : Converter<T, Object>;

        method HasDefaultValue(instance : T; memberName : String) : Boolean;
        method HasDefaultValue(instance : T; memberInfo : MemberInfo) : Boolean;

        constructor(useHiddenMembers : Boolean);
        constructor(defaultInstance : T; useHiddenMembers : Boolean);    
  end;

implementation

constructor DefaultValueInspector<T>(useHiddenMembers : Boolean);
begin
    var ctorInfo := typeOf(T).GetConstructor([]);
    constructor(ctorInfo.Invoke([]) as T, useHiddenMembers);
end;

constructor DefaultValueInspector<T>(defaultInstance : T; useHiddenMembers : Boolean);
begin
    var bf := iif(useHiddenMembers, 
                  BindingFlags.NonPublic)
              or BindingFlags.Public
              or BindingFlags.Instance;

    for mi in typeOf(T).GetMembers(bf) do
        case mi.MemberType of
            MemberTypes.Field :
            with matching fi := FieldInfo(mi) do
            begin
                MembersByName.Add(fi.Name, fi);
                GettersByMember.Add(mi, obj -> fi.GetValue(obj));
            end;
            MemberTypes.Property :
            with matching pi := PropertyInfo(mi) do
                if pi.GetIndexParameters().Length = 0 then
                begin
                   MembersByName.Add(pi.Name, pi);
                   GettersByMember.Add(mi, obj -> pi.GetValue(obj, nil));
                end;
        end;

    for g in GettersByMember do
        with val := g.Value(DefaultInstance) do
            if assigned(val) then 
                DefaultValuesByMember.Add(g.Key, val);
end;

class method DefaultValueInspector<T>.GetMemberErrorMessage(memberName : String) : String;
begin
    exit "The member '" + memberName + "' does not exist in type " + typeOf(T).FullName 
         + " or it has indexers."
end;

method DefaultValueInspector<T>.get_DefaultValueByName(memberName : String): Object;
begin
    var mi := GetMember(memberName);
    DefaultValuesByMember.TryGetValue(mi, out result);
end;

method DefaultValueInspector<T>.get_DefaultValueByMember(memberInfo : MemberInfo) : Object;
begin
    if not DefaultValuesByMember.TryGetValue(memberInfo, out result) then
        raise new ArgumentException(GetMemberErrorMessage(memberInfo.Name),
                                    "memberName"); 
end;

method DefaultValueInspector<T>.GetGetMethod(memberName : String) : Converter<T, Object>;
begin
    var mi := GetMember(memberName);
    exit GetGetMethod(mi);
end;

method DefaultValueInspector<T>.GetGetMethod(memberInfo : MemberInfo) : Converter<T, Object>;
begin
    if not GettersByMember.TryGetValue(memberInfo, out result) then
        raise new ArgumentException(GetMemberErrorMessage(memberInfo.Name),
                                    "memberName"); 
end;

method DefaultValueInspector<T>.GetMember(memberName : String) : MemberInfo;
begin
    if not MembersByName.TryGetValue(memberName, out result) then
        raise new ArgumentException(GetMemberErrorMessage(memberName),
                                    "memberName"); 
end;

method DefaultValueInspector<T>.HasDefaultValue(instance : T; memberName : String) : Boolean;
begin
    var getter := GetGetMethod(memberName);
    var instanceValue := getter(instance);
    exit Equals(DefaultValueByName[memberName], instanceValue);
end;

method DefaultValueInspector<T>.HasDefaultValue(instance : T; memberInfo : MemberInfo) : Boolean;
begin
    var getter := GetGetMethod(memberInfo);
    var instanceValue := getter(instance);
    exit Equals(DefaultValueByMember[memberInfo], instanceValue);
end;

Der Compiler kann eingestellt werden, um eine Warnung zu erzeugen, wenn Sie versuchen, eine Variable zu verwenden, bevor sie einen Wert zuweisen. Ich habe die Standardeinstellung und das, wie es sich verhalten.

Ist die folgende Hilfe:

bool isAssigned = (myOtherInt == default(int));
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top