web-dev-qa-db-ja.com

宣言で物理的に入力された変数の名前を取得するにはどうすればよいですか?

可能性のある複製:
C#で関数に渡された変数名を見つける

以下のクラスには、フィールドcityが含まれています。

クラス宣言で入力されたフィールドの名前を動的に決定する必要があります。つまり、オブジェクトcityのインスタンスから文字列「city」を取得する必要があります。

DoSomething()でTypeを調べてこれを実行しようとしましたが、デバッガーでTypeの内容を調べたときに見つけられません。

出来ますか?

public class Person
{
  public string city = "New York";

  public Person()
  {
  }


  public void DoSomething()
  {
    Type t = city.GetType();

    string field_name = t.SomeUnkownFunction();
    //would return the string "city" if it existed!
  }
}

以下の回答の一部の人々は、なぜ私がこれをしたいのかと私に尋ねました。その理由は次のとおりです。

私の実世界の状況では、都市の上にカスタム属性があります。

[MyCustomAttribute("param1", "param2", etc)]
public string city = "New York";

この属性は他のコードで必要です。属性を取得するには、リフレクションを使用します。そして、リフレクションコードでは、文字列「city」を入力する必要があります

MyCustomAttribute attr;
Type t = typeof(Person);

foreach (FieldInfo field in t.GetFields())
{

  if (field.Name == "city")
  {
    //do stuff when we find the field that has the attribute we need
  }

}

現在、これはタイプセーフではありません。 Personのフィールド宣言で変数「city」を「workCity」に変更した場合、文字列を更新することを知らない限り、この行は失敗します

if (field.Name == "workCity")
//I have to make this change in another file for this to still work, yuk!
{
}

したがって、物理的に入力せずにこのコードに文字列を渡す方法を見つけようとしています。

はい、Personで文字列定数(またはそのようなもの)として宣言できますが、それでも2回入力することになります。

ふう!説明するのは大変でした!!

ありがとう

これに*たくさん*答えたすべての人に感謝します。ラムダ式をよりよく理解するための新しい道を私に送りました。そして、新しい質問を作成しました。

34
Petras

たぶん、これが必要です。正常に動作します。

私はこれを見つけました ここ

static void Main(string[] args)
{
    var domain = "matrix";
    Check(() => domain);
    Console.ReadLine();
}

static void Check<T>(Expression<Func<T>> expr)
{
    var body = ((MemberExpression)expr.Body);
    Console.WriteLine("Name is: {0}", body.Member.Name);
    Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member)
   .GetValue(((ConstantExpression)body.Expression).Value));
}

出力は次のようになります。

名前: 'domain' 
値: 'matrix' 
50
Luciano

私はこれが古い質問であることを知っていますが、私は同じことを達成しようとしていて、グーグルはここに私を送りました。何時間も経って、ようやく道を見つけました。他の誰かがこれが役立つことを願っています。

実際には、これを達成するためのより多くの方法があります。

static void Main(string[] args) 
{
  GetName(new { var1 });
  GetName2(() => var1);
  GetName3(() => var1);
}

static string GetName<T>(T item) where T : class 
{
  return typeof(T).GetProperties()[0].Name;
}

static string GetName2<T>(Expression<Func<T>> expr) 
{
  return ((MemberExpression)expr.Body).Member.Name;
}

static string GetName3<T>(Func<T> expr) 
{
  return expr.Target.GetType().Module.ResolveField(BitConverter.ToInt32(expr.Method.GetMethodBody().GetILAsByteArray(), 2)).Name;
}

最初が最速です。最後の2つは、最初のものより約20倍遅いです。

http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html

37
Markos

この場合のcityは、タイプstringの-​​インスタンスです。 .GetType()を呼び出すと、特定の都市インスタンスの知識なしを持つ実際の文字列型を返します。

ここでコードに「city」を文字列リテラルとして単に入力できないのは、それが必要な場合、私は苦労しています。おそらく、この値を何に使用したいのか、どのような状況でDoSomething()関数を呼び出すのかを共有すると役立つでしょう。

現時点では、あなたが本当にやりたいことはPersonクラス全体を反映して、そのクラスのフィールドのリストを取得することです。

public void DoSomething()
{
    MemberInfo[] members = this.GetType().GetMembers();

    // now you can do whatever you want with each of the members,
    // including checking their .Name properties.
}

さて、あなたの編集に基づいて、私はあなたのためにもう少し持っています。

次のように、実行時に属性で装飾されたフィールドの名前を見つけることができます。

Type t = typeof(Person);
foreach (MemberInfo member in t.GetMembers()
          .Where(m => 
                m.GetCustomAttributes(typeof(MyCustomAttribute)).Any()  ) )
{
    // "member" is a MemberInfo object for a Peson member that is 
    // decorated with your attribute
}

必要に応じて、最初のGetMembers()呼び出しでバインドフラグを使用して、フィールドだけに制限することもできます。

7
Joel Coehoorn

「つまり、オブジェクトcityのインスタンスから文字列「city」を取得する必要があります。」フィールドの値からフィールド名を取得しようとしていますか。たとえば、2つのPersonオブジェクトがあり、1つは都市「ニューヨーク」、もう1つは都市「ロンドン」である場合、「都市」を返す関数を探していますか?これは、ダイナミックという意味ですか?


現在の設計では、FieldInfoのフィールドの名前を文字列と常に比較する必要があります。代わりにこれを分離して、属性の一部としてリフレクション中に比較目的で使用する識別子を保持する場合はどうなりますか。このようなもの:

 public enum ReflectionFields
{
    CITY = 0,
    STATE,
    Zip,    
    COUNTRY

}

[AttributeUsage(AttributeTargets.Field,AllowMultiple=false)]
public class CustomFieldAttr : Attribute
{
    public ReflectionFields Field { get; private set; }
    public string MiscInfo { get; private set; }

    public CustomFieldAttr(ReflectionFields field, string miscInfo)
    {
        Field = field;
        MiscInfo = miscInfo;
    }
}

public class Person
{
    [CustomFieldAttr(ReflectionFields.CITY, "This is the primary city")]
    public string _city = "New York";

    public Person()
    {
    }
    public Person(string city)
    {
        _city = city;
    }

}

public static class AttributeReader<T> where T:class
{
    public static void Read(T t)
    {
        //get all fields which have the "CustomFieldAttribute applied to it"
        var fields = t.GetType().GetFields().Where(f => f.GetCustomAttributes(typeof(CustomFieldAttr), true).Length == 1);

        foreach (var field in fields)
        {
            var attr = field.GetCustomAttributes(typeof(CustomFieldAttr), true).First() as CustomFieldAttr;
            if (attr.Field == ReflectionFields.CITY)
            {
                //You have the field and you know its the City,do whatever processing you need.
                Console.WriteLine(field.Name);
            }
        }            
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        PPerson p1 = new PPerson("NewYork");
        PPerson p2 = new PPerson("London");
        AttributeReader<PPerson>.Read(p1);
        AttributeReader<PPerson>.Read(p2);

}
 }

Personの_cityフィールドを別の名前に自由に変更できるようになりました。リフレクションを使用するコードは、フィールドに設定された属性の初期化の一部としてReflectionFields列挙値セットを使用してフィールドを識別しようとするため、呼び出しコードは引き続き機能します。

4
Abhijeet Patel

はい、可能です!!!

これを試してみてください...

  public string DoSomething(object city)
  {
       return city.GetType().GetProperty("Name",typeof(string)).GetValue(city,null);
  }
4
shahjapan

ここに二つのこと。

第一に、上記の誰かが指摘したように、PersonではなくstringのTypeを取得しています。したがって、typeof(Person).GetMembers()はメンバーのリストを取得します。

2番目、さらに重要なことは、属性の目的を誤解しているようです。一般に、属性は、特定の処理のためにメンバーをマークするため、または追加情報を追加するために使用されます。ここでは、名前を使用して必要な処理を示し、属性を使用してパラメータを指定しています。これは、メタファーなどの混合です。

Abhijeetの答えがより適切です。フィールドをa cityフィールドとしてマークし、それで好きなことをします。私が反対するのは、列挙ではなく異なる属性クラスを使用することです。

何かのようなもの:

    public class MyAttribute : Attribute
    {

    }

    [AttributeUsage(AttributeTargets.Field)]
    public class MyCityAttribute : MyAttribute
    {
    }

    [AttributeUsage(AttributeTargets.Field]
    public class MyNameAttribute: MyAttribute
    {
    }

    public class Person
    {

        [MyCity]
        public string city = "New York";

        [MyCity]
        public string workCity = "Chicago";

        [MyName]
        public string fullName = "John Doe";

        public Person()
        {
        }


        public void DoSomething()
        {
            Type t = typeof(Person);
            FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public);

            foreach (var field in fields)
            {
                MyAttribute[] attributes = field.GetCustomAttributes(typeof(MyAttribute));
                if (attributes.Count > 0)
                {
                    if (attributes[0] is MyCityAttribute)
                    {
                        //Dosomething for city
                        break;
                    }

                    if (attributes[0] is MyNameAttribute)
                    {
                        //Dosomething for names
                        break;
                    }
                }
            }
        }
    }

これにより、MyCityとMyNameのそれぞれの処理のコンテキストでより意味のある異なるパラメーターを使用できます。

上記の「ユーク」コメントで、頭に釘を打ったと思います。変数の名前を変更した場合に文字列定数を変更する必要があるということは、何か間違っていることを示しています。

1
Darren Clark
t.GetField("city", BindingFlags.Public | BindingFlags.Instance);

または、GetFields()を呼び出してすべてのフィールドを取得できます

0
Canton

クラスPersonでget typeを呼び出す必要があります。以下の答えのようにクラスのフィールドを繰り返します

0
Preet Sangha

これは不可能です(実際はそうですが、いくつかのハックとラムダの使用が関係していると思います)。 Personに関する属性を保存し、属性の名前を簡単に取得できるようにする場合は、Dictionary<TKey, TValue>名前空間のSystem.Collections.Genericを使用することをお勧めします。

また、辞書をラップするパブリックプロパティをいつでも作成できます。

public class Person
{
  Dictionary<string, string> attributes = new Dictionary<string, string();
  public string City
  {
    get { return attributes["city"]; }
    set { attributes["city"] = value; }
  }

  public Person()
  {
    City = "New York";
  }
}

また、attributes.Keysを使用してすべての属性のリストを取得できます。

0
Samuel

あなたがやろうとしていることに似ているので、この投稿を見てください:

関数に渡された変数名を見つける

(特にKonrad Rudolphの答え)別のアプローチは、属性のパラメーターの1つとして「都市」を追加し、後でそれをフィッシングすることです。

0
pbz

FieldInfoオブジェクトのコレクションを既にループしています。それらの属性を探して、属性を含むFieldInfoが見つかったら、必要な属性があります。次に、.Nameを呼び出します。

system.reflection.fieldinfo.attributes

0
brendanjerwin