Get Attribute info with generics - c#

actually, I can make a relation between a table field and a variable by doing this inside my OE:
public class MyOE
{
[Column("AGE_FIELD")]
public int ageField { get; set; }
}
My OE class just need to use this other class:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
public class ColumnAtt : Attribute
{
private string name;
public string Name
{
get { return name; }
}
public ColumnAtt (string name)
{
this.name = name;
}
}
Well, using the code above, Im doing a generic method that I will need to get the "Column" value. How I could do that?
Here is my method:
public void CompareTwoObjectsAndSaveChanges<TObjectType>(TObjectType objectA, TObjectType objectB )
{
if(objectA.GetType() == objectB.GetType())
{
foreach (var prop in objectA.GetType().GetProperties())
{
if(prop.GetValue(objectA, null) != prop.GetValue(objectB, null))
{
string colvalue = "";//Here I need to get the Column value of the attribute.
string nameOfPropertyThatChanges = prop.Name;
string objectAValue = prop.GetValue(objectA, null).ToString();
string objectBValue = prop.GetValue(objectB, null).ToString();
}
}
}
}

Use reflection:
private static void Main(string[] args)
{
MyOE zz = new MyOE { ageField = 45 };
foreach (PropertyInfo property in zz.GetType().GetProperties())
{
// Gets the first attribute of type ColumnAttribute for the property
// As you defined AllowMultiple as true, you should loop through all attributes instead.
var attribute = property.GetCustomAttributes(false).OfType<ColumnAttribute>().FirstOrDefault();
if (attribute != null)
{
Console.WriteLine(attribute.Name); // Prints AGE_FIELD
}
}
Console.ReadKey();
}

You need to use reflection to get the attributes applied to your object. If you know the attribute is always going to be ColumnAtt, then you can do something like this to get the value:
public void CompareTwoObjectsAndSaveChanges<TObjectType>(TObjectType objectA, TObjectType objectB )
{
if(objectA.GetType() == objectB.GetType())
{
foreach (var prop in objectA.GetType().GetProperties())
{
if(prop.GetValue(objectA, null) != prop.GetValue(objectB, null))
{
// Get the column attribute
ColumnAttr attr = (ColumnAttr)objectA.GetType().GetCustomAttributes(typeof(ColumnAttr), false).First();
string colValue = attr.Name;
string nameOfPropertyThatChanges = prop.Name;
string objectAValue = prop.GetValue(objectA, null).ToString();
string objectBValue = prop.GetValue(objectB, null).ToString();
}
}
}
}
This makes use of the GetCustomAttributes(...) method.

Try this:
var columnAtt = prop.GetCustomAttributes(typeof(CustomAtt),true).Cast<ColumnAtt>().FirstOrDefault();
(fixed)

Related

Cast a property to its actual type dynamically using reflection (where actual type is generic) v2

This is a slightly modified version of the question I asked before here.
The actual position is like below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace cns01
{
class Program
{
public class DataFieldInfo2<T>
{
public bool IsSearchValue { get; set; } = false;
public T Value { get; set; }
}
public class SmartRowVertrag
{
public DataFieldInfo2<int> ID { get; set; }
public DataFieldInfo2<string> VSNR { get; set; }
}
static void Main(string[] args)
{
SmartRowVertrag tester = new SmartRowVertrag();
tester.ID = new DataFieldInfo2<int>() { Value = 777, IsSearchValue = false };
tester.VSNR = new DataFieldInfo2<string>() { Value = "234234234", IsSearchValue = true };
var g = RetData3(tester);
}
public static object RetData3(object fsr)
{
object retVal = new object();
var props = fsr.GetType().GetProperties();
foreach (var prop in props)
{
PropertyInfo propInfo = prop.GetType().GetProperty("IsSearchValue"); // <-- here I get null
propInfo = prop.PropertyType.GetProperty("IsSearchValue"); // here I get a propertyInfo, but...
dynamic property = propInfo.GetValue(fsr, null); // ...<-- here fires error
var result = property.IsSearchValue;
if (result == true)
{
// doThis
}
}
return retVal;
}
}
}
I do need to get the boolean value stored in IsSearchValue to to accomplish things.
Thanks beforehand.
I mostly appreciate any helping answer.
fsr is the SmartRowVertrag. You're trying to get the IsSearchValue from that - but it doesn't exist.
Instead, you'll need to call GetValue twice - once on prop, passing in fsr (so which is equivalent to using fsr.ID or dsr.VSNR) and then once on propInfo passing in the result of that first call.
Next, your dynamic use is trying to get IsSearchValue on the result of propInfo.GetValue() - you're trying to evaluate something.IsSearchValue.IsSearchValue, effectively.
Here's corrected code for that part:
public static object RetData3(object fsr)
{
object retVal = new object();
var props = fsr.GetType().GetProperties();
foreach (var prop in props)
{
PropertyInfo propInfo = prop.PropertyType.GetProperty("IsSearchValue");
if (propInfo != null)
{
object fieldInfo = prop.GetValue(fsr);
object isSearchValue = propInfo.GetValue(fieldInfo);
if (isSearchValue.Equals(true))
{
Console.WriteLine($"{prop.Name} has a searchable field");
}
}
}
return retVal;
}
You could avoid using reflection twice though if you had either a non-generic interface that DataFieldInfo2<T> implemented, or a base class. For example:
public interface IDataFieldInfo
{
bool IsSearchValue { get; }
}
public class DataFieldInfo2<T> : IDataFieldInfo
{
...
}
You could then make your reflection code much clearer:
public static object RetData3(object fsr)
{
var fieldProperties = fsr.GetType().GetProperties()
.Where(prop => typeof(IDataFieldInfo).IsAssignableFrom(prop.PropertyType));
foreach (var fieldProperty in fieldProperties)
{
var field = (IDataFieldInfo) fieldProperty.GetValue(fsr);
if (field.IsSearchValue)
{
Console.WriteLine($"{fieldProperty.Name} is a search value field");
}
}
// We don't actually know what you're trying to return
return null;
}

How to set nested property value using FastMember

I get an exception when I try to set a nested member Property using FastMember. For example when having these classes
public class A
{
public B First { get; set; }
}
public class B
{
public string Second { get; set; }
}
and I want to set First.Second of an instance to "hello".
var b = new B{ Second = "some value here" };
var a = new A{ First = b };
var accessor = ObjectAccessor.Create(a);
accessor["First.Second"] = value; // this does not work and gives ArgumentOutOfRangeException
I can't split it up into ["First"]["Second"] because I don't know the depth at this point. Is there a magical access for nested properties or do I have to split the hierarchy myself?
I solved the problem recursively using an Extension Method this way:
public static class FastMemberExtensions
{
public static void AssignValueToProperty(this ObjectAccessor accessor, string propertyName, object value)
{
var index = propertyName.IndexOf('.');
if (index == -1)
{
accessor[propertyName] = value;
}
else
{
accessor = ObjectAccessor.Create(accessor[propertyName.Substring(0, index)]);
AssignValueToProperty(accessor, propertyName.Substring(index + 1), value);
}
}
}
... and this is started as follows:
ObjectAccessor.Create(a).AssignValueToProperty("First.Second", "hello")
You need to traverse the object graph using multiple ObjectAccessor instances.
public static void UseFastMember()
{
var b = new B { Second = "some value here" };
var a = new A { First = b };
var value = "hello";
var a_accessor = ObjectAccessor.Create(a);
var first = a_accessor["First"];
var b_accessor = ObjectAccessor.Create(first);
b_accessor["Second"] = value;
}
Hats off to #Beachwalker for the inspiration. But should you be using TypeAccessor as opposed to ObjectAccessor this is an extension method I've had much success with:
public static class TypeAccessorExtensions
{
public static void AssignValue<T>(this TypeAccessor accessor, T t, MemberSet members, string fieldName, object fieldValue)
{
var index = fieldName.IndexOf('.');
if (index == -1)
{
if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
accessor[t, fieldName] = fieldValue;
}
else
{
string fieldNameNested = fieldName.Substring(0, index);
var member = members.FirstOrDefault(m => string.Equals(m.Name, fieldNameNested, StringComparison.OrdinalIgnoreCase));
if (member != null)
{
var nestedAccesor = TypeAccessor.Create(member.Type);
var tNested = accessor[t, fieldNameNested];
if (tNested == null)
{
tNested = Activator.CreateInstance(member.Type);
accessor[t, fieldNameNested] = tNested;
}
nestedAccesor.AssignValue(tNested, nestedAccesor.GetMembers(), fieldName.Substring(index + 1), fieldValue);
}
}
}
}

How to get specific data from custom attribute on multiple properties

I'm new to attributes so what I'm trying to achieve might not be possible. From my understanding, attributes are used to bind data at compile time to specific methods, properties, classes, etc.
My attribute is very simple and defined as follows:
public class NowThatsAnAttribute : Attribute
{
public string HeresMyString { get; set; }
}
I have two properties that are using the same custom attribute like so:
public class MyClass
{
[NowThatsAnAttribute(HeresMyString = "A" )]
public string ThingyA
{
get;
set;
}
[NowThatsAnAttribute(HeresMyString = "B" )]
public string ThingyB
{
get;
set;
}
}
That is all working fine and dandy, but I am trying to access the attributes of these properties from another method and set certain data based on the attribute's value.
Here is what I am doing right now:
private void MyMethod()
{
string something = "";
var props = typeof (MyClass).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(NowThatsAnAttribute)));
//this is where things get messy
//here's sudo code of what I want to do
//I KNOW THIS IS NOT VALID SYNTAX
foreach(prop in props)
{
//my first idea was this
if(prop.attribute(NowThatsAnAttribute).HeresMyString == "A")
{
something = "A";
}
else if(prop.attribute(NowThatsAnAttribute).HeresMyString == "B")
{
something = "B";
}
//my second idea was this
if(prop.Name == "ThingyA")
{
something = "A";
}
else if(prop.Name == "ThingyB")
{
something = "B";
}
}
//do stuff with something
}
The problem is that something is always being set to "B" which I know is due to looping through the properties. I'm just unsure how to access the value associated with the attribute of a specific property. I have a feeling there's a painfully obvious way of doing this that I'm just not seeing.
I've tried using the StackFrame but ended in near the same spot I'm at now.
NOTE: Using .Net 4.0.
NOTE2: MyMethod is part of an interface which I am unable to edit so I can't pass in any parameters or anything.
Any help is appreciated.
if you pass a name of specific property, you will be able to take correct attribute value:
private string MyMethod(string propName)
{
PropertyInfo pi = typeof (MyClass).GetProperty(propName);
if (pi == null)
return null;
var a = (NowThatsAnAttribute)pi.GetCustomAttribute(typeof(NowThatsAnAttribute));
if (a!=null)
return a.HeresMyString;
return null;
}
MyMethod("ThingyA") returns A
Reflection also gives you the names of the properties.
Type t = typeof(MyClass);
PropertyInfo[] props = t.GetProperties();
foreach (PropertyInfo p in props) {
NowThatsAnAttribute attr = p.GetCustomAttributes(false)
.OfType<NowThatsAnAttribute>()
.FirstOrDefault();
if (attr != null) {
string propName = p.Name;
string attrValue = attr.HeresMyString;
switch (propName) {
case "ThingyA":
//TODO: Do something for ThingyA
break;
case "ThingyB":
//TODO: Do something for ThingyB
break;
default:
break;
}
}
}
You could also add the info to a dictionary:
var dict = new Dictionary<string, string();
...
if (attr != null) {
string propName = p.Name;
string attrValue = attr.HeresMyString;
dict.Add(propName, attrValue);
}
...
Getting value of some prop
string value = dict["ThingyA"];
You could do a generic method like this:
static object GetAttributeValue<T, A>(string attribName, string propName = "") where A : Attribute
{
object[] attribs = null;
if (string.IsNullOrEmpty(propName))
attribs = typeof(T).GetCustomAttributes(true);
else
{
PropertyInfo pi = typeof(T).GetProperty(propName);
if (pi == null)
return null;
attribs = pi.GetCustomAttributes(true);
}
A a = null;
foreach (object attrib in attribs)
{
if (attrib is A)
{
a = attrib as A;
break;
}
}
if (a != null)
{
var prop = a.GetType().GetProperty(attribName);
return prop.GetValue(a, null);
}
return null;
}
You would then call it like this:
object value = GetPropertyAttributeValue<MyClass,NowThatsAnAttribute>("HeresMyString", "ThingyA");
So your "MyMethod" could look like this:
private void MyMethod()
{
string something = "";
var props = typeof (MyClass).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(NowThatsAnAttribute)));
//this is where things get messy
//here's sudo code of what I want to do
//I KNOW THIS IS NOT VALID SYNTAX
foreach(prop in props)
{
string attribVal = object value = GetPropertyAttributeValue<MyClass,NowThatsAnAttribute>("HeresMyString", prop.Name);
//my first idea was this
if(attribVal == "A")
{
something = "A";
}
else if(attribVal == "B")
{
something = "B";
}
}
//do stuff with something
}
This would allow you to reuse the same method to get attribute values for different attributes on different properties(or attributes directly on the class by skipping the second parameter) from different classes.

Access to the value of a Custom Attribute

I've got this custom attribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited = true)]
class MethodTestingAttibute : Attribute
{
public string Value{ get; private set; }
public MethodTestingAttibute (string value)
{
this.Value= value;
}
}
To be used like this:
[MethodTestingAttibute("2")]
public int m1() {return 3; }
And my difficulty is to take the value of "2" of MethodTestingAttibute
object result = method.Invoke(obj, new Type[] {}); // here i get the return
Now I want to compare this result to the value of MethodTestingAttibute. How can I do that? I'm trying to go up to this road but without success:
method.GetCustomAttributes(typeof(MethodTestAttibute), true)[0]...
What is the proper way to get access to the field of the custom attribute?
var attribute =
(MethodTestingAttibute)
typeof (Vehicles)
.GetMethod("m1")
.GetCustomAttributes(typeof (MethodTestingAttibute), false).First();
Console.WriteLine(attribute.Value);
With my custom attribute:
[AttributeUsage(AttributeTargets.Method)]
public class AttributeCustom : Attribute
{
public string MyPropertyAttribute { get; private set; }
public AttributeCustom(string myproperty)
{
this.MyPropertyAttribute = myproperty;
}
}
I create a method for to get attribute with his values:
public static AttributeCustom GetAttributeCustom<T>(string method) where T : class
{
try
{
return ((AttributeCustom)typeof(T).GetMethod(method).GetCustomAttributes(typeof(AttributeCustom), false).FirstOrDefault());
}
catch(SystemException)
{
return null;
}
}
With a example class (must be not static because T is generic)
public class MyClass
{
[AttributeCustom("value test attribute")])
public void MyMethod()
{
//...
}
}
Usage:
var customAttribute = GetAttributeCustom<MyClass>("MyMethod");
if (customAttribute != null)
{
Console.WriteLine(customAttribute.MyPropertyAttribute);
}
Cast the object to MethodTestingAttibute:
object actual = method.Invoke(obj, null);
MethodTestingAttibute attribute = (MethodTestingAttibute)method.GetCustomAttributes(typeof(MethodTestAttribute), true)[0];
string expected = attribute.Value;
bool areEqual = string.Equals(expected, actual != null ? actual.ToString() : null, StringComparison.Ordinal);
To get the value of an attribute property, just cast the object returned by GetCustomAttributes():
{
string val;
object[] atts = method.GetCustomAttributes(typeof(MethodTestAttibute), true);
if (atts.Length > 0)
val = (atts[0] as MethodTestingAttibute).Value;
}
Necromancing.
For those that still have to maintain .NET 2.0, or those that want to do it without LINQ:
public static object GetAttribute(System.Reflection.MemberInfo mi, System.Type t)
{
object[] objs = mi.GetCustomAttributes(t, true);
if (objs == null || objs.Length < 1)
return null;
return objs[0];
}
public static T GetAttribute<T>(System.Reflection.MemberInfo mi)
{
return (T)GetAttribute(mi, typeof(T));
}
public delegate TResult GetValue_t<in T, out TResult>(T arg1);
public static TValue GetAttributValue<TAttribute, TValue>(System.Reflection.MemberInfo mi, GetValue_t<TAttribute, TValue> value) where TAttribute : System.Attribute
{
TAttribute[] objAtts = (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), true);
TAttribute att = (objAtts == null || objAtts.Length < 1) ? default(TAttribute) : objAtts[0];
// TAttribute att = (TAttribute)GetAttribute(mi, typeof(TAttribute));
if (att != null)
{
return value(att);
}
return default(TValue);
}
Example usage:
System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});
or in your case simply
MethodInfo mi = typeof(Vehicles).GetMethod("m1");
string aValue = GetAttributValue<MethodTestingAttibute, string>(mi, a => a.Value);
Check the code here http://msdn.microsoft.com/en-us/library/bfwhbey7.aspx
Excerpt:
// Get the AClass type to access its metadata.
Type clsType = typeof(AClass);
// Get the type information for Win32CallMethod.
MethodInfo mInfo = clsType.GetMethod("Win32CallMethod");
if (mInfo != null)
{
// Iterate through all the attributes of the method.
foreach(Attribute attr in
Attribute.GetCustomAttributes(mInfo)) {
// Check for the Obsolete attribute.
if (attr.GetType() == typeof(ObsoleteAttribute))
{
Console.WriteLine("Method {0} is obsolete. " +
"The message is:",
mInfo.Name);
Console.WriteLine(" \"{0}\"",
((ObsoleteAttribute)attr).Message);
}
// Check for the Unmanaged attribute.
else if (attr.GetType() == typeof(UnmanagedAttribute))
{
Console.WriteLine(
"This method calls unmanaged code.");
Console.WriteLine(
String.Format("The Unmanaged attribute type is {0}.",
((UnmanagedAttribute)attr).Win32Type));
AClass myCls = new AClass();
myCls.Win32CallMethod();
}
}
}

Convert object namespace and name to object

I need to call SetSettings() and using the 3 elements in splitSettings, set EncodeAudio to False.
How would I go about doing that? Convert the property of a object to who's name I have in a string.
I realize I could do with with a switch statement of all my settings but there has to be a more dynamic way to go about doing this.
namespace SettingsLib
{
public class Settings
{
public Boolean EncodeAudio { get; set; }
}
}
namespace Service
{
void SetSettings()
{
string[] splitSettings = { "SettingsLib.Settings", "EncodeAudio", "False" };
// Need to set EncodeAudio to False in SettingsLib.Settings
}
}
Yes I have a instance of Settings
Say:
Settings settingManager = new Settings();
I am trying to do is dynamically set EncodeAudo to False by using elements of splitSettings
settingManager.EncodeAudio = False;
Thanks to the help of TBohnen.jnr
I came to this answer:
public void setProperty(object containingObject, string propertyName, object newValue)
{
foreach (PropertyInfo p in containingObject.GetType().GetProperties())
{
if (p.Name == propertyName)
{
p.SetValue(containingObject, Convert.ChangeType(newValue, p.PropertyType), null);
}
}
}
EDIT Tested it with int, bool, double and string and it worked, also added a check to make sure that the property exists and throws an exception of it doesn't (Might want to change Exception type)
EDIT 2: Temporary solution, will add more typenames to the convert method or alternatively if somebody can suggest a more dynamic way of casting it (If not then I assume you will have to know all of the types that will be used)?
EDIT3 Stole the convert method from another answer in question (Chris Taylor ), thanks :-)
public void setProperty(object containingObject, string propertyName, object newValue)
{
if (containingObject.GetType().GetProperties().Count(c => c.Name == propertyName) > 0)
{
var type = containingObject.GetType().GetProperties().First(c => c.Name == propertyName).PropertyType;
object val = Convert(type,(string)newValue);
containingObject.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, containingObject, new object[] { val });
}
else
{
throw new KeyNotFoundException("The property: " + propertyName + " was not found in: " + containingObject.GetType().Name);
}
}
public object convert(System.Type type, string value)
{
return Convert.ChangeType(value, type);
}
Taken from http://www.haslo.ch/blog/setproperty-and-getproperty-with-c-reflection/
Was interested to see if this works, create a quick test:
class testSettings
{
public bool SetBool { get; set; }
public void setProperty(object containingObject, string propertyName, object newValue)
{
if (containingObject.GetType().GetProperties().Count(c => c.Name == propertyName) > 0)
{
containingObject.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, containingObject, new object[] { newValue });
}
else
{
throw new KeyNotFoundException("The property: " + propertyName + " was not found in: " + containingObject.GetType().Name);
}
}
}
static void Main(string[] args)
{
testSettings ts = new testSettings();
ts.SetBool = false;
ts.setProperty(ts, "SetBool", true);
Console.WriteLine(ts.SetBool.ToString());
Console.Read();
}
The output is true, not entirely sure if it will convert all types correctly though.
As others have mentioned, you should consider making your SettingsLib class static. And you might also need to handle the conversion of values from strings to the target types. Here is a simple example how this would work.
namespace Service
{
class Program
{
static void Main(string[] args)
{
string[] splitSettings = { "SettingsLib.Settings", "EncodeAudio", "False" };
SetProperty(splitSettings[0], splitSettings[1], splitSettings[2]);
}
static void SetProperty(string typeName, string propertyName, object value)
{
var type = Type.GetType(typeName);
if (type == null)
{
throw new ArgumentException("Unable to get type", "typeName");
}
var pi = type.GetProperty(propertyName);
if (pi == null)
{
throw new ArgumentException("Unable to find property on type", "propertyName");
}
object propertyValue = value;
if (propertyValue != null)
{
// You might need more elaborate testing here to ensure that you can handle
// all the various types, you might need to special case some types here
// but this will work for the basics.
if (pi.PropertyType != propertyValue.GetType())
{
propertyValue = Convert.ChangeType(propertyValue, pi.PropertyType);
}
}
pi.SetValue(null, propertyValue, null);
}
}
}
namespace SettingsLib
{
public static class Settings
{
public static bool EncodeAudio { get; set; }
}
}
Maybe you should mark your settable properties as static and then try to set the values using Reflection:
namespace SettingsLib
{
public static class Settings
{
public static bool EncodeAudio { get; set; }
}
}
namespace Service
{
void SetSettings()
{
string[] splitSettings = { "SettingsLib.Settings", "EncodeAudio", "False" };
dynamic property = Type.GetType(splitSettings[0]).GetProperty(splitSettings[1]);
property = splitSettings[2];
}
}

Categories

Resources