7

クラス宣言があるとします。たとえば、次のようになります。


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

さて、リフレクション (またはその他の手段) を使用して、myInt にはデフォルト値が割り当てられているが、myOtherInt には割り当てられていないと推測できる汎用コードの方法はありますか? 明示的なデフォルト値で初期化する場合と、暗黙的なデフォルト値のままにする場合の違いに注意してください (デフォルトでは、myOtherInt は 0 に初期化されます)。

私自身の調査によると、これを行う方法はないように見えますが、あきらめる前にここで質問したいと思いました.

[編集]

null 許容型と参照型であっても、null として残されているものと、明示的に null に初期化されているものを区別したいと考えています。これは、初期化子を持つフィールドは「オプション」であり、他のフィールドは「必須」であると言えるようにするためです。現時点では、属性を使用してこれを行う必要があります。この場合、情報の冗長性に悩まされています。

4

12 に答える 12

18

私はあなたのコードをコンパイルしてILDASMにロードし、これを得ました

.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

ldc.i4.7およびstfld int32 dummyCSharp.MyClass::myIntは、myInt フィールドのデフォルト値を設定するための指示のように見えることに注意してください。

したがって、そのような割り当ては、実際にはコンストラクターで追加の割り当てステートメントとしてコンパイルされます。

このような割り当てを検出するには、リフレクションを使用して MyClass のコンストラクター メソッドの IL を反映し、stfld(フィールドの設定?) コマンドを探す必要があります。


編集:コンストラクターに明示的に割り当てを追加すると:

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

    public MyClass()
    {
        myOtherInt = 8;
    }
}

ILDASM にロードすると、次のようになりました。

.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

私が追加した myOtherInt の余分な代入は、Object クラスのコンストラクターの呼び出し後に追加されたことに注意してください。

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

それで、あなたはそれを持っています、

IL で Object クラスのコンストラクターを呼び出すに行われた割り当ては、既定値の割り当てです。

それに続くものは、クラスの実際のコンストラクター コード内のステートメントです。

ただし、より広範なテストを行う必要があります。

ps 楽しかったです :-)

于 2008-11-07T12:32:45.197 に答える
4

この動作には、null 許容の int を検討することをお勧めします。

class MyClass
{
  int? myInt = 7;
  int? myOtherInt = null;
}
于 2008-11-07T12:25:58.453 に答える
3

デフォルト値は、他の値と同様です。これら 2 つのケースを区別する方法はありません。

int explicitly = 0;
int implicitly;

どちらの場合も、値 0 を指定します。一方の方法では、入力を省くことができます。魔法の「デフォルトの初期化されていない値」はありません-両方ともゼロです。それらはまったく同じであることがわかります。しかし、あなたがこれを熟考しているという事実は、あなたが良いアイデアの軌道から真剣に外れていることを示しています. 何してるの?あなたの特定のニーズは何ですか?あなたは間違った質問をしています;)

于 2008-11-07T12:31:29.270 に答える
1

これを一般的なランタイム機能として構築したい場合は、次のようにします。スカラー型の場合、デフォルト値属性を作成し、それを使用してデフォルト性を判断します。

これがタスクの部分的な解決策です-もっと良いかもしれないと確信していますが、私はそれをノックアウトしました:

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();
        }
    }
}
于 2008-11-07T15:52:40.380 に答える
1

オプションのパラメーターに null 許容型を使用する値型は機能するはずです。オプションでない場合、文字列を空に初期化することもできます。

int mandatoryInt;
int? optionalInt;

ただし、これは少し汚いと思いますが、これを行う明確な方法として属性に固執します。

于 2008-11-07T12:43:51.383 に答える
1

これは最も単純な解決策ではないかもしれません...

DefaultValue 属性を使用して、次のように値を設定できます。

System.ComponentModel と System.Reflection をインポートする

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

次に、リフレクションでデフォルト値を回復します。

PropertyInfo prop = this.GetType().GetProperty("MyNumber");
MessageBox.Show(((DefaultValueAttribute)(prop.GetCustomAttributes(typeof(DefaultValueAttribute), true).GetValue(0))).Value.ToString());
于 2008-11-07T13:37:56.157 に答える
1

値と初期化フラグを含む汎用構造体を作成するのはどうですか?

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
}

次に、初期化について知る必要があるメンバーの代わりにこれを使用します。怠惰な未来や約束のかなり基本的なバリエーションです。

于 2008-11-07T15:25:14.897 に答える
0

フィールドをプライベート/保護されたプロパティでラップできます。設定されているかどうかを知りたい場合は、プライベート フィールド (例: _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; }
    }
}
于 2008-11-07T15:10:20.933 に答える
0

これが必要な場合は、下部のコードを確認してください。
これは Oxygene[1] で書かれていますが、問題ないことを願っています。

[1]または Delphi Prism は現在どのように呼ばれているか


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);

出力:

値: 1; inst1HasDefault: 真; inst2HasDefault False
値: 2; inst1HasDefault: 偽; 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;
于 2008-11-07T17:26:08.507 に答える
0

このアプローチでは、プロパティの get/set プロセスを使用します。

    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
    }

これは、1 つのフィールドに対して大量のコードです。こんにちは、スニペットです。

于 2008-11-07T13:07:30.603 に答える
-1

値を代入する前に変数を使用しようとすると、警告を生成するようにコンパイラを設定できます。デフォルト設定があり、それがどのように動作するか。

于 2008-11-07T12:31:33.343 に答える
-2

次のことが役立ちますか:

bool isAssigned = (myOtherInt == default(int));
于 2008-11-07T12:36:19.917 に答える