Reflection - Get Attribute Name and Value on Property

Reflection - get attribute name and value on property

Use typeof(Book).GetProperties() to get an array of PropertyInfo instances. Then use GetCustomAttributes() on each PropertyInfo to see if any of them have the Author Attribute type. If they do, you can get the name of the property from the property info and the attribute values from the attribute.

Something along these lines to scan a type for properties that have a specific attribute type and to return data in a dictionary (note that this can be made more dynamic by passing types into the routine):

public static Dictionary<string, string> GetAuthors()
{
Dictionary<string, string> _dict = new Dictionary<string, string>();

PropertyInfo[] props = typeof(Book).GetProperties();
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
AuthorAttribute authAttr = attr as AuthorAttribute;
if (authAttr != null)
{
string propName = prop.Name;
string auth = authAttr.Name;

_dict.Add(propName, auth);
}
}
}

return _dict;
}

How to get Attribute Value in C# for the class property

Given

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = true)]
public sealed class ColumnNameAttribute : Attribute
{
public readonly string Column;

public ColumnNameAttribute(string columnName)
{
this.Column = columnName;
}
}

(by convention attributes should have a name ending in Attribute, and note that I've restricted the AttributeTargets to Propertyes and Fields) you can

public static class ColumnsCache<T>
{
public static readonly IReadOnlyDictionary<MemberInfo, string> Columns = BuildColumnsDictionary();

public static Dictionary<MemberInfo, string> BuildColumnsDictionary()
{
var dict = new Dictionary<MemberInfo, string>();

var members = typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property);

foreach (MemberInfo member in members)
{
var attr = member.GetCustomAttribute<ColumnNameAttribute>(true);

if (attr != null)
{
dict.Add(member, attr.Column);
}
}

return dict;
}
}

(note the trick: we are caching the list of column names (and of fields/properties having the ColumnNameAttribute) through the usage of a generic static class. The .NET runtime will create various distinct ColumnsCache<T1>, ColumnsCache<T2>, ColumnsCache<T3>, each one with a different dictionary of columns)

Then you can

var cols = ColumnsCache<Comment>.Columns;
var colNames = cols.Values;

The cols variable will reference a dictionary MemberInfo -> string (colum name), while the colNames is a IEnumerable<string> with only the column names. If you want to use reflection with a MemberInfo you have to check if the MemberInfo is a FieldInfo or a PropertyInfo, cast it and use the FieldInfo or PropertyInfo.

Get raw value of property attribute using Reflection

Since attribute constructor information are stored as metadata as well you can get the required information by calling the GetCustomAttributesData method. Have a look at this simple example:

class Program
{
[PasswordPropertyText]
public string Password1 { get; set; }

[PasswordPropertyText(true)]
public string Password2 { get; set; }

[PasswordPropertyText(false)]
public string Password3 { get; set; }

static void Main(string[] args)
{
var props = typeof(Program).GetProperties();
foreach(var prop in props)
{
var attributeData = prop.GetCustomAttributesData().First(x => x.AttributeType == typeof(PasswordPropertyTextAttribute));
Console.WriteLine($"{prop.Name}: {(attributeData.ConstructorArguments.Cast<CustomAttributeTypedArgument?>().FirstOrDefault()?.Value ?? true)}");
}
Console.ReadLine();
}
}

Output:

Password1: True
Password2: True
Password3: False

C# get property value from object using custom attribute

In your example, you're getting the customattributes of the class instead of the properties.

Here is an example:

private object GetValueBySectionFlag(object obj, string flagName)
{
// get the type:
var objType = obj.GetType();

// iterate the properties
var prop = (from property in objType.GetProperties()
// iterate it's attributes
from attrib in property.GetCustomAttributes(typeof(SectionFlagAttribute), false).Cast<SectionFlagAttribute>()
// filter on the name
where attrib.Name == flagName
// select the propertyInfo
select property).FirstOrDefault();

// use the propertyinfo to get the instance->property value
return prop?.GetValue(obj);
}

Note: this will return only the first property which contains the SectionFlagAttribute with the right name. You could modify the method to return multiple values. (like a collection of propertyname/value)


Usage:

// a test instance.
var obj = new ApplicationStatusFlags { IceAttributeStatement = true };

// get the value by SectionFlag name
var iceAttributeStatement = GetValueBySectionFlag(obj, "APPLICANTSTATEMENT");

If the returned value is null then the flag is not found or the property's value is null.

Get attribute values from property and list values without knowing the attribute type

If you mean "given an attribute that takes one parameter, give me that value", for example:

[DisplayName("abc")] <===== "abc"
[Browsable(true)] <===== true

then this is easiest in .NET 4.5, via the new CustomAttributeData API:

using System.ComponentModel;
using System.Reflection;

public static class Program
{
static void Main()
{
PropertyInfo prop = typeof(Foo).GetProperty("Bar");
var val = GetPropertyAttributes(prop, "DisplayName");
}
public static object GetPropertyAttributes(PropertyInfo prop, string attributeName)
{
// look for an attribute that takes one constructor argument
foreach(CustomAttributeData attribData in prop.GetCustomAttributesData())
{
string typeName = attribData.Constructor.DeclaringType.Name;
if(attribData.ConstructorArguments.Count == 1 &&
(typeName == attributeName || typeName == attributeName + "Attribute"))
{
return attribData.ConstructorArguments[0].Value;
}
}
return null;
}
}

class Foo
{
[DisplayName("abc")]
public string Bar { get; set; }
}

Reflection blazor - Get attribute name + value

I already have this need to check for attributes to use DisplayName to render a label and Required to add a red * to the label, and I didn't know how to do this, I tried the normal way of doing it, but no success.

But then I remembered that Blazor already does this with ValidationMessage because it gets the attributes from a property and validates it... so I decided to check the source code of it.

Digging very deep, I found this function which explains how to do what we need.

First of all, it have a Expression<Func<T>> parameter, which in blazor is the For property of ValidationMessage, so we can see here that probably isn't possible to do it with a binded value or just passing it like Foo="@Foo" (if it was possible, they would probably have done that way), so we need to have another property that will pass that type

e.g.

<TestComponent @bind-Value="testString" Field=@"(() => testString)" />

Now continuing with the code from that function, it will get the Body of the expression and do some checkings to get and make sure you are passing a property.

And then there is this line.

fieldName = memberExpression.Member.Name;

And if you take a look at memberExpression.Member and call GetCustomAttributes you will have exactly what we need, all the custom attributes of the proprety.

So now all we need is just loop the custom attributes and do what ever you want.

Here is a simplified version to get the CustomAttribute of the property returned in the Expression<Func<T>>

private IEnumerable<CustomAttributeData> GetExpressionCustomAttributes<T>(Expression<Func<T>> accessor)
{
var accessorBody = accessor.Body;

// Unwrap casts to object
if (accessorBody is UnaryExpression unaryExpression
&& unaryExpression.NodeType == ExpressionType.Convert
&& unaryExpression.Type == typeof(object))
{
accessorBody = unaryExpression.Operand;
}

if (!(accessorBody is MemberExpression memberExpression))
{
throw new ArgumentException($"The provided expression contains a {accessorBody.GetType().Name} which is not supported. {nameof(FieldIdentifier)} only supports simple member accessors (fields, properties) of an object.");
}

return memberExpression.Member.GetCustomAttributes();
}

For you example, here is how to solve it

.razor

<TestComponent @bind-Value="testString" Field="(() => testString)" />

@code {
[MaxLength(10)]
private string testString;
}

TestComponent.razor

<input type="text" @bind="Value" @bind:event="oninput" />

@code {
[Parameter] public Expression<Func<string>>Field { get; set; }
[Parameter] public string Value { get; set; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }

protected override void OnInitialized()
{
base.OnInitialized();
if (Field != null)
{
var attrs = GetExpressionCustomAttributes(Field);

foreach (var attr in attrs)
{
if(attr is MaxLengthAttribute maxLengthAttribute)
{
// Do what you want with maxLengthAttribute
}
}
}
}

private IEnumerable<CustomAttributeData> GetExpressionCustomAttributes<T>(Expression<Func<T>> accessor)
{
var accessorBody = accessor.Body;

// Unwrap casts to object
if (accessorBody is UnaryExpression unaryExpression
&& unaryExpression.NodeType == ExpressionType.Convert
&& unaryExpression.Type == typeof(object))
{
accessorBody = unaryExpression.Operand;
}

if (!(accessorBody is MemberExpression memberExpression))
{
throw new ArgumentException($"The provided expression contains a {accessorBody.GetType().Name} which is not supported. {nameof(FieldIdentifier)} only supports simple member accessors (fields, properties) of an object.");
}

return memberExpression.Member.GetCustomAttributes();
}
}

If you only have one attribute, you can also call memberExpression.Member.GetCustomAttributes<Attribute>() to get a list of that attribute type.

TL;DR

Add a new property to the component

[Parameter] public Expression<Func<T>>Field { get; set; }

Use this gist helper functions to get the attribute(s) you want.

Get property name by attribute and its value

Use GetCustomAttributes to find the attributes, and LINQ to filter and get an anonymous object which holds the Property and the Attribute.

Use PropertyInfo.GetValue to read the actual value.

But please beware that the reflection calls are pretty expensive:

var propertiesWithAttribute = typeof(Entity).GetProperties()
// use projection to get properties with their attributes -
.Select(pi => new { Property = pi, Attribute = pi.GetCustomAttributes(typeof(MyAttribute), true).FirstOrDefault() as MyAttribute})
// filter only properties with attributes
.Where(x => x.Attribute != null)
.ToList();

foreach (Entity entity in collection)
{
foreach (var pa in propertiesWithAttribute)
{
object value = pa.Property.GetValue(entity, null);
Console.WriteLine($"PropertyName: {pa.Property.Name}, PropertyValue: {value}, AttributeName: {pa.Attribute.GetType().Name}");
}
}

Retrieve name and value of property has a specific attribute value

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.

Get attribute from record

You're looking in the wrong place: when using the "braceless" record syntax in C#, attributes placed on members are actually parameter attributes.

You can get the DisplayAttribute from [Display] String FirstName like so:

ParameterInfo[] ctorParams = typeof(TestRecord)
.GetConstructors()
.Single()
.GetParameters();

DisplayAttribute firstNameDisplayAttrib = ctorParams
.Single( p => p.Name == "FirstName" )
.GetCustomAttributes()
.OfType<DisplayAttribute>()
.Single();

Reflection - Get all properties values which marked certain attribute

Not shure what result you are trying to get but this should help:

        RspServer server = new RspServer();

Type ClassType = server.GetType();

Dictionary<string, string> Description2Value = new Dictionary<string, string>();

foreach (PropertyInfo pi in ClassType.GetProperties().Where(pi => Attribute.IsDefined(pi, typeof(Description))))
{
Description d = (Description)pi.GetCustomAttributes(typeof(Description), false)[0];
string PropVal = (string)pi.GetValue(server);

Description2Value.Add(d.Value, PropVal);
}


Related Topics



Leave a reply



Submit