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 Property
es and Field
s) 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
Virtual Member Call in a Constructor
Listing All Permutations of a String/Integer
C# String Replace Does Not Actually Replace the Value in the String
Best Way to Implement Keyboard Shortcuts in a Windows Forms Application
Difference Between Task and Thread
Using Xpath With Default Namespace in C#
What Is an "Index Out of Range" Exception, and How to Fix It
JavaScriptserializer - Json Serialization of Enum as String
How to Call Stored Procedure in Entity Framework 6 (Code-First)
Json.Net: How to Deserialize Without Using the Default Constructor
Watermark/Hint/Placeholder Text in Textbox
Filtering on Include in Ef Core
Download Excel File Via Ajax MVC
Cannot Convert from List≪Derivedclass≫ to List≪Baseclass≫
Pre & Post Increment Operator Behavior in C, C++, Java, & C#
What Is an Indexoutofrangeexception/Argumentouto
frangeexception and How to Fix It
Extension Method and Dynamic Object
Why Would You Use Expression≪Func≪T≫≫ Rather Than Func≪T≫