web-dev-qa-db-fra.com

Comment obtenir le nom d'une variable telle qu'elle a été physiquement tapée dans sa déclaration?

Duplicata possible:
Recherche du nom de variable transmis à une fonction en C #

La classe ci-dessous contient la ville du champ.

J'ai besoin de déterminer dynamiquement le nom du champ tel qu'il est tapé dans la déclaration de classe, c'est-à-dire que j'ai besoin d'obtenir la chaîne "city" à partir d'une instance de la ville d'objet.

J'ai essayé de le faire en examinant son type dans DoSomething (), mais je ne le trouve pas lors de l'examen du contenu du type dans le débogueur.

C'est 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!
  }
}

Certaines personnes dans leurs réponses ci-dessous m'ont demandé pourquoi je voulais faire cela. Voici pourquoi.

Dans ma situation réelle, il y a un attribut personnalisé au-dessus de la ville.

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

J'ai besoin de cet attribut dans un autre code. Pour obtenir l'attribut, j'utilise la réflexion. Et dans le code de réflexion, je dois taper la chaîne "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
  }

}

Maintenant, ce n'est pas un type sûr. Si j'ai changé la variable "city" en "workCity" dans ma déclaration de champ dans Person, cette ligne échouerait à moins que je sache mettre à jour la chaîne

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

J'essaie donc de trouver un moyen de passer la chaîne à ce code sans le taper physiquement.

Oui, je pourrais le déclarer comme une constante de chaîne dans Person (ou quelque chose comme ça) mais ce serait encore le taper deux fois.

Phew! C'était difficile à expliquer !!

Merci

Merci à tous ceux qui ont répondu * beaucoup *. Cela m'a envoyé sur une nouvelle voie pour mieux comprendre les expressions lambda. Et cela a créé une nouvelle question.

34
Petras

Vous en avez peut-être besoin. Fonctionne bien.

J'ai trouvé cela ici .

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

La sortie sera:

 Le nom est: 'domaine' 
 La valeur est: 'matrice' 
50
Luciano

Je sais que c'est une vieille question, mais j'essayais d'y parvenir et Google m'a envoyé ici. Après plusieurs heures, j'ai finalement trouvé un moyen. J'espère que quelqu'un d'autre trouvera cela utile.

Il existe en fait plus de façons d'accomplir cela:

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

Le premier est le plus rapide. Les 2 derniers sont environ 20 fois plus lents que le 1er.

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

37
Markos

city dans ce cas est un instance de type string. Lorsque vous appelez .GetType(), vous retournez le type de chaîne réel, qui a aucune connaissance du tout de votre instance de ville particulière.

J'ai du mal à voir pourquoi vous ne pouvez pas simplement taper "ville" dans le code sous forme de chaîne littérale ici, si c'est ce dont vous avez besoin. Il serait peut-être utile que vous partagiez à quoi vous voulez utiliser cette valeur et dans quelles circonstances vous appellerez votre fonction DoSomething().

Pour le moment, ma meilleure supposition est que ce que vous voulez vraiment faire est de refléter toute la classe Person pour obtenir une liste des champs de cette classe:

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

D'accord, sur la base de votre montage, j'ai encore plus pour vous.

Vous pouvez trouver le nom des champs décorés avec votre attribut au moment de l'exécution comme ceci:

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
}

Vous pouvez également utiliser des indicateurs de liaison dans le premier appel GetMembers () pour le limiter aux seuls champs, si vous le souhaitez.

7
Joel Coehoorn

Vous avez mentionné "c'est-à-dire que je dois obtenir la chaîne" city "d'une instance de la ville objet." Cherchez-vous à obtenir le nom du champ à partir de la valeur du champ. Par exemple: s'il existe un objet 2 personnes, l'un avec la ville "New York" et l'autre avec la ville "Londres", recherchez-vous la fonction pour renvoyer "ville". Est-ce ce que vous entendez par dynamique?


Avec votre conception actuelle, vous devrez toujours comparer le nom du champ de FieldInfo à une chaîne. Que se passe-t-il si vous le découplez à la place afin de conserver l'identifiant à utiliser à des fins de comparaison pendant la réflexion dans le cadre de l'attribut. Quelque chose comme ça:

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

}
 }

Vous pouvez maintenant renommer librement le champ _city de Person en quelque chose d'autre et votre code appelant fonctionnera toujours puisque le code utilisant la réflexion tente d'identifier le champ en utilisant l'ensemble de valeurs d'énumération ReflectionFields dans le cadre de l'initialisation de l'attribut défini sur le champ.

4
Abhijeet Patel

Oui c'est possible !!!

Essayez ceci ...

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

Deux choses ici.

Premièrement, comme l'a souligné quelqu'un ci-dessus, vous obtenez le type de chaîne, pas de personne. Donc, typeof (Person) .GetMembers () vous donnera la liste des membres.

Numéro deux, et plus important encore, il semble que vous compreniez mal le but des attributs. En général, les attributs sont utilisés pour marquer un membre pour un traitement spécifique ou pour ajouter des informations supplémentaires. Ici, vous utilisez le nom pour indiquer le traitement que vous souhaitez, et l'attribut pour spécifier les paramètres, qui est le mélange des métaphores, ou quelque chose.

La réponse d'Abhijeet est plus appropriée, vous marquez le champ comme a champ ville, puis faites ce que vous aimez avec. Là où je ne suis pas d'accord, c'est que j'utiliserais différentes classes d'attributs, plutôt qu'une énumération.

Quelque chose comme:

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

Cela vous permettrait d'utiliser différents paramètres pour MyCity vs MyName qui auraient plus de sens dans le contexte du traitement de chacun.

Je pense qu'avec votre commentaire 'yuk' ci-dessus, vous avez frappé le clou sur la tête. Le fait que vous deviez changer une constante de chaîne si vous renommez votre variable est un indicateur que vous faites quelque chose de mal.

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

ou vous pouvez appeler GetFields () pour obtenir tous les champs

0
Canton

Vous devez appeler get type sur la classe Person. L'itération des champs de la classe comme dans la réponse ci-dessous

0
Preet Sangha

Ce n'est pas possible (je pense que c'est en fait mais invoque plusieurs hacks et utilise des lambdas). Si vous souhaitez stocker des attributs sur un Person et pouvoir obtenir facilement le nom de l'attribut, je suggère d'utiliser un Dictionary<TKey, TValue> du System.Collections.Generic espace de noms.

Et vous pouvez toujours rendre les propriétés publiques qui enveloppent le dictionnaire.

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

Et vous pouvez obtenir une liste de tous les attributs avec attributes.Keys.

0
Samuel

Jetez un œil à ce post car il ressemble à ce que vous essayez de faire:

Recherche du nom de variable passé à une fonction

(en particulier la réponse de Konrad Rudolph) Une autre approche pourrait être d'ajouter simplement "ville" comme l'un des paramètres de l'attribut et de le repérer plus tard.

0
pbz

Vous parcourez déjà la collection d'objets FieldInfo. Recherchez votre attribut sur ceux-ci et lorsque vous trouvez le FieldInfo qui contient votre attribut, vous avez celui que vous voulez. Appelez ensuite .Name dessus.

system.reflection.fieldinfo.attributes

0
brendanjerwin