C# 你如何得到一个变量的名字,因为它是在它的声明中物理输入的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/716399/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:17:44  来源:igfitidea点击:

How do you get a variable's name as it was physically typed in its declaration?

c#variablesreflection

提问by Petras

Possible Duplicate:
Finding the Variable Name passed to a Function in C#

可能的重复:
在 C# 中查找传递给函数的变量名

The class below contains the field city.

下面的类包含字段城市。

I need to dynamically determine the field's name as it is typed in the class declaration i.e. I need to get the string "city" from an instance of the object city.

我需要动态确定字段的名称,因为它是在类声明中键入的,即我需要从对象 city 的实例中获取字符串“city”。

I have tried to do this by examining its Type in DoSomething() but can't find it when examining the contents of the Type in the debugger.

我试图通过在 DoSomething() 中检查它的 Type 来做到这一点,但是在调试器中检查 Type 的内容时找不到它。

Is it possible?

是否可以?

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!
  }
}

Some people in their answers below have asked me why I want to do this. Here's why.

有些人在下面的回答中问我为什么要这样做。这是为什么。

In my real world situation, there is a custom attribute above city.

在我的现实世界中,城市上方有一个自定义属性。

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

I need this attribute in other code. To get the attribute, I use reflection. And in the reflection code I need to type the string "city"

我在其他代码中需要这个属性。为了获取属性,我使用反射。在反射代码中,我需要输入字符串“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
  }

}

Now this isn't type safe. If I changed the variable "city" to "workCity" in my field declaration in Person this line would fail unless I knew to update the string

现在这不是类型安全的。如果我在 Person 的字段声明中将变量“city”更改为“workCity”,除非我知道更新字符串,否则此行将失败

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

So I am trying to find some way to pass the string to this code without physically typing it.

因此,我试图找到某种方法将字符串传递给此代码,而无需实际键入它。

Yes, I could declare it as a string constant in Person (or something like that) but that would still be typing it twice.

是的,我可以在 Person(或类似的东西)中将它声明为字符串常量,但仍然会输入两次。

Phew! That was tough to explain!!

呼!这很难解释!!

Thanks

谢谢

Thanks to all who answered this * a lot*. It sent me on a new path to better understand lambda expressions. And it created a new question.

感谢所有回答这个问题的人*很多*。它让我走上了一条更好地理解 lambda 表达式的新途径。它创造了一个新问题。

回答by Joel Coehoorn

cityin this case is an instanceof type string. When you call .GetType()you return the actual string type, which has no knowledge at allof your particular city instance.

city在这种情况下是一个类型的实例string。当您调用时,.GetType()您会返回实际的字符串类型,它对您的特定城市实例一无所知

I'm having a hard time seeing why you can't just type "city" in the code as a string literal here, if that's what you need. Perhaps it would help if you shared what you want to use this value for and in what circumstances you will call your DoSomething()function.

我很难理解为什么你不能在代码中输入“city”作为字符串文字,如果你需要的话。如果你分享你想使用这个值的目的以及在什么情况下你会调用你的DoSomething()函数,这可能会有所帮助。

At the moment, my best guess is that what you really want to do is reflect the entire Personclass to get a list of the fields in that class:

目前,我最好的猜测是您真正想要做的是反映整个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.
}


Okay, based on your edit I have some more for you.

好的,根据你的编辑,我还有一些给你。

You can find the name of fields that are decorated with your attribute at run-time like this:

您可以在运行时找到用您的属性修饰的字段的名称,如下所示:

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
}

You can also use binding flags in the first GetMembers() call to limit it to just fields, if you want.

如果需要,您还可以在第一个 GetMembers() 调用中使用绑定标志将其限制为仅字段。

回答by Canton

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

or you can call GetFields() to get all fields

或者您可以调用 GetFields() 来获取所有字段

回答by Preet Sangha

You need to call get type on the class Person. The iterate the fields of the class as in the answer below

您需要在 Person 类上调用 get 类型。迭代类的字段,如下面的答案

回答by Samuel

This is not possible (I think it actually is but involes several hacks and using lambdas). If you want to store attributes about a Personand be able to get the name of the attribute easily, I suggest using a Dictionary<TKey, TValue>from the System.Collections.Genericnamespace.

这是不可能的(我认为它实际上是,但涉及一些技巧和使用 lambdas)。如果你想存储有关的属性Person,并能够轻松获得属性的名称,我建议使用一个Dictionary<TKey, TValue>System.Collections.Generic命名空间。

And you can always make public properties that wrap the dictionary.

并且您始终可以创建包装字典的公共属性。

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";
  }
}

And you can get a list of all attributes with attributes.Keys.

您可以使用attributes.Keys.

回答by pbz

Have a look at this post as it looks similar to what you're trying to do:

看看这篇文章,因为它看起来与你想要做的很相似:

Finding the variable name passed to a function

查找传递给函数的变量名

(especially Konrad Rudolph's answer) Another approach could be to just add "city" as one of the parameters in the attribute and fish that out later.

(尤其是 Konrad Rudolph 的回答)另一种方法可能是在属性中添加“城市”作为参数之一,然后再将其删除。

回答by brendanjerwin

You are already looping through the collection of FieldInfoobjects. Look for your attribute on those and when you find the FieldInfothat contains your attribute, you have the one you want. Then call .Nameon it.

您已经在遍历FieldInfo对象集合。在这些上查找您的属性,当您找到FieldInfo包含您的属性的属性时,您就拥有了您想要的属性。然后调用.Name它。

system.reflection.fieldinfo.attributes

system.reflection.fieldinfo.attributes

回答by Abhijeet Patel

You mentioned "i.e. I need to get the string "city" from an instance of the object city." Are you looking to get the field name from the value of the field. For example:If there are 2 Person object one with city "New York" and the other with city "London", are you looking for the function to return "city". Is this what you mean by dynamic?

您提到“即我需要从对象城市的实例中获取字符串“城市”。” 您是否希望从字段的值中获取字段名称。例如:如果有 2 个 Person 对象,一个城市为“纽约”,另一个城市为“伦敦”,您是否正在寻找返回“城市”的函数。这就是你所说的动态吗?



With your current design you will always need to compare the name of the field from the FieldInfo against a string. What if you instead decouple this so that you hold the identifier to use for comparison purposes during reflection as part of the attribute. Something like this:

对于您当前的设计,您将始终需要将 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);

}
 }

You can now freely rename _city field of Person to something else and your calling code will still work since the code using reflection is trying to identify the field using the ReflectionFields enum value set as part of initialization of the attribute set on the field.

您现在可以自由地将 Person 的 _city 字段重命名为其他内容,并且您的调用代码仍然可以工作,因为使用反射的代码正在尝试使用 ReflectionFields 枚举值来标识该字段,该值设置为字段上属性集初始化的一部分。

回答by Darren Clark

Two things here.

这里有两件事。

Number one, as someone above pointed out, you're getting the Type for string, not for Person. So typeof(Person).GetMembers() will get you the list of members.

第一,正如上面有人指出的,你得到的是字符串的类型,而不是人的类型。因此 typeof(Person).GetMembers() 将为您提供成员列表。

Number two, and more importantly, it looks like you're misunderstanding the purpose of attributes. In general attributes are used to mark a member for specific processing or to add additional information. Here you're using the name to indicate what processing you want, and the attribute to specify parameters, which is mixing of metaphors, or something.

第二,更重要的是,您似乎误解了属性的用途。通常,属性用于标记成员以进行特定处理或添加附加信息。在这里,您使用名称来指示您想要的处理方式,并使用属性来指定参数,这是隐喻的混合,或者其他什么。

Abhijeet's answer is more appropriate, you mark the field as acity field, then do what you like with it. Where I disagree is that I would use different attribute classes, rather than an enumeration.

Abhijeet的回答是比较合适的,你标记字段作为一个城市字段,然后做你喜欢它。我不同意的地方是我会使用不同的属性类,而不是枚举。

Something like:

就像是:

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

This would allow you to use different parameters for MyCity vs MyName that would make more sense in the context of processing each.

这将允许您为 MyCity 和 MyName 使用不同的参数,这在处理每个参数的上下文中更有意义。

I think with your 'yuk' comment above, you hit the nail on the head. That you would have to change a string constant if you rename your variable is an indicator that you're doing something wrong.

我认为你上面的“yuk”评论,你一针见血。如果重命名变量,则必须更改字符串常量表明您做错了。

回答by Darren Clark

Maybe you need this. Works fine.

也许你需要这个。工作正常。

I found this here.

我在这里找到了这个。

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

Output will be:

输出将是:

Name is: 'domain'
Value is: 'matrix'

回答by shahjapan

Yes its possible !!!

是的,有可能!!!

Try this out...

试试这个...

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