你如何获得变量的名称,因为它在声明中是物理输入的?

时间:2009-04-04 02:51:55

标签: c# variables reflection

  

可能重复:
  Finding the Variable Name passed to a Function in C#

下面的课程包含字段城市。

我需要在类声明中输入时动态确定字段的名称 即我需要从对象城市的实例中获取字符串“city”。

我试过通过检查其在DoSomething()中的类型来尝试这样做,但在检查调试器中的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中的字符串常量(或类似的东西),但仍然会输入两次。

唷!这很难解释!!

感谢

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

11 个答案:

答案 0 :(得分:49)

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

我发现了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));
}

输出将是:

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

答案 1 :(得分:37)

我知道这是一个老问题,但我试图达到同样的目的,谷歌把我送到了这里。几个小时后,我终于找到了办法。我希望其他人会觉得这很有用。

实际上有更多方法可以实现这一目标:

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个比第1个慢约20倍。

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

答案 2 :(得分:7)

在这种情况下,

citystring类型的实例。当您致电.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()调用中使用绑定标志将其限制为仅字段。

答案 3 :(得分:4)

你提到“即我需要从对象城市的一个实例中获取字符串”city“。” 您是否希望从字段的值中获取字段名称。 例如:如果有2个人对象一个城市“纽约”而另一个城市“伦敦”,你是否正在寻找返回“城市”的功能。这是你的意思吗?


使用当前设计,您始终需要将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 :(得分:4)

是的可能!!!

试试这个......

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

答案 5 :(得分:1)

这里有两件事。

第一,正如上面所指出的那样,你得到的是字符串的类型,而不是Person。所以typeof(Person).GetMembers()会为你提供成员列表。

第二,更重要的是,看起来你误解了属性的目的。通常,属性用于标记成员以进行特定处理或添加其他信息。在这里,您使用名称来指示您想要的处理,以及用于指定参数的属性,即隐喻的混合或其他内容。

Abhijeet的答案更合适,你将该字段标记为 a 城市字段,然后用它做你喜欢的事情。我不同意的是我会使用不同的属性类,而不是枚举。

类似的东西:

    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的不同参数,这些参数在处理每个参数时更有意义。

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

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

这是不可能的(我认为它实际上是偶然的几个黑客并使用lambdas)。如果您想存储有关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获取所有属性的列表。

答案 9 :(得分:0)

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

Finding the variable name passed to a function

(尤其是Konrad Rudolph的回答)另一种方法可能是将“city”添加为属性中的一个参数,然后将鱼添加出去。

答案 10 :(得分:0)

您已经遍历FieldInfo个对象的集合。在这些上查找您的属性,当您找到包含您的属性的FieldInfo时,您就拥有了所需的属性。然后拨打.Name就可以了。

system.reflection.fieldinfo.attributes

相关问题