0

Assume there is an attribute such as:

public class ValueAttribute : Attribute
{
  public int Val;
  public ValueAttribute(int val)
  {
    Val = val;
  }
}

And it is applied to a class:

public class Person
{
  [Value(10)]
  public string FirstName;
  [Value(20)]
  public string LastName;
  [Value(30)]
  public string Age;
}

How would I go about efficiently retrieving a PropertyInfo (preferably without loop/iteration) of the first occurrence of a property that has Value(20)?

5
  • 1
    IMHO you could not make it without iterating the properties and checking its individual Value Commented May 11, 2020 at 6:48
  • I was afraid of that. Commented May 11, 2020 at 6:54
  • 2
    Something is going to iterate, if not your code, then framework code. You can cache the attributes after you find them to substantially improve subsequent lookup performance. Also, attributes should be sealed in performance critical code. Commented May 11, 2020 at 6:57
  • Would either of you mind making this into an answer that I can accept? Cheers Commented May 11, 2020 at 8:35
  • You have Fields and Not Properties (so you'd want FieldInfos). Also, I do not believe that the order of the fields/properties is guaranteed (it may be an implementation detail) Commented May 11, 2020 at 10:22

2 Answers 2

2

First of all, you have fields and not properties, so if anything you'd need to get the attributes from a FieldInfo object. Second, there is no way to do what you want without some form of iteration. If you are worried about having to look it up each time you could cache the results per type.

public static class ValueHelper<T>
{
     private static readonly Dictionary<int, FieldInfo> FieldsByValue;

     static ValueHelper()
     {
         FieldsByValue = typeof(T)
                 .GetFields()
                 .Select(f => new { 
                       Field = f, 
                       Att = f.GetCustomAttribute<ValueAttribute>() 
                  })
                 .Where(c => c.Att != null)
                 .GroupBy(c => c.Att.Val, (val, flds) => new { 
                      Value = val, 
                      Field = flds.First() 
                  })
                 .ToDictionary(c => c.Value, c => c.Field);
     }

     public static FieldInfo GetFieldByValue(int value)
     {
          if (FieldsByValue.TryGetValue(value, out var field)) 
             return field;
          // not found, return null...
          // throw exception, etc
          return null;
     } 

} 

This can then be used like:

var field = ValueHelper<Person>.GetFieldByValue(20);

The reflection only occurs once (thanks to the static constructor) and then is cached in a lookup table for further accesses.

If you really have properties, then replace FieldInfo with PropertyInfo and GetFields with GetProperties.

Note: I am not sure if the order of fields/properties returned from reflection is guaranteed. It may be the case that they are returned in source order, but that may very well be an implementation detail.

Sign up to request clarification or add additional context in comments.

1 Comment

I really appreciate how thorough your answer is. For future reference, starting an answer off with "first of all" and then going on to correct someone may leave them feeling like you are talking down to them like a child. I'm going to assume that wasn't your intent. Cheers and thanks for your answer; accepting it!
0

You could use below code

var attr = (ValueAttribute)(typeof(Person)
          .GetField("FirstName")
          .GetCustomAttributes(false)
          .GetValue(0));
Console.WriteLine(attr.Val);  

1 Comment

This code assumes that a) all fields have that attribute set and b) there is only one attribute set in those fields. Both things are dangerous assumptions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.