How do access the attributes of a method's Parameters - c#

How do you determine if a parameter has a custom attribute attached to it?
I thought this test case would pass:
[TestCase("")]
public void TestParameterAttribute([NotRequired]string theString)
{
var result = false;
foreach (var attribute in theString.GetType().GetCustomAttributes(true))
{
if (attribute.GetType() == (typeof(NotRequiredAttribute)))
{
result = true;
}
}
Assert.That(result, Is.True);
}

It requires a little bit more work.
[TestCase("")]
public void TestParameterAttribute([NotRequired]string theString)
{
var method = MethodInfo.GetCurrentMethod();
var parameter = method.GetParameters()[0];
var result = false;
foreach (var attribute in parameter.GetCustomAttributes(true))
{
if (attribute.GetType() == (typeof(NotRequiredAttribute)))
{
result = true;
}
}
Assert.That(result, Is.True);
}

theString.GetType() gets a reference to the Type representing a string. Calling GetCustomAttributes on it will look in the string class for those attributes.
What you want to do.. is get the attributes for the parameters in the current method. Maybe something like this:
var result = false;
foreach (var parameter in MethodInfo.GetCurrentMethod().GetParameters())
{
if (parameter.GetCustomAttributes().Any(x => x.GetType() == typeof (NotRequiredAttribute)))
result = true;
}

Also you can use Generic version of GetCustomAttribute method:
parameter.GetCustomAttribute<NotRequiredAttribute>() != null

Related

How can calling a function with an alias name or attribute name?

I have a method like this.
[CodeName("Bar")]
public bool IsWidow(Guid applicantId)
And I want to call this method with a code that I have as an attribute above this function.
something like this
bar();
How it is possible to call the IsWidow() method when I call bar() method?
Using my powers of deduction I think you might want to call the method based on the CodeName attribute. In which case you can use reflection to get the attributes.
The method below allows you to call a method that has an attribute that matches the CodeName that you want. pars is an optional list of parameters that need to be passed to the method, this is a flaw in the plan really because you will need to know the signature of the method to know what parameters to pass in.
object? CallMethod(object target, string codeName, params object[] pars)
{
foreach (var m in target.GetType().GetMethods())
{
var att = m.GetCustomAttributes().OfType<CodeNameAttribute>().FirstOrDefault();
if (att != null)
{
if (att.Name == codeName)
{
return m.Invoke(target, pars);
}
}
}
}
i'm not absolutely sure what you are trying to achieve, so its hard to know whether the above function is really of any use
Here a working Example:
Here the example class
public class myClass
{
[myAtt("Bar")]
public void Mehtod1(string someText, int ID)
{
//Inside method one
}
[myAtt("someOtherText")]
public void Mehtod2()
{
//Inside method 2
}
public class myAtt : Attribute
{
public string Name { get; set; }
public myAtt(string name)
{
Name = name;
}
}
}
And here the working example: (Look at example 1. Example one is exactly what you are looking for)
public void DoWork()
{
myClass myClassObj = new myClass(); //Create an object of your class
/* Main Example */
//This is what you are looking for
var allMethods2 = myClassObj.GetType().GetMethods(); //Get all methods in the class
foreach (var method in allMethods2) //loop
{
var attributes = method.GetCustomAttributes(false); //custom attributes
if (attributes.Length > 0)
{
var attribute =
attributes.FirstOrDefault(att => att is myClass.myAtt); //Get the attribute you are looking for
if (attribute != null && ((myClass.myAtt)attribute).Name == "Bar") //check null & check if the 'inside' of the attribute is correct
{
var parameters = method.GetParameters();
var objToPass = new List<object>();
foreach (var parameterInfo in parameters) //Loop through method params
{
var toAdd = new object(); //Object to pass after the loop
if (parameterInfo.ParameterType == typeof(string))
{
//Param is string
switch (parameterInfo.Name)
{
case "someText":
toAdd = "Some text to add";
break;
default:
//Log?
toAdd = "Don't know the attribute...";
break;
}
}
else if (parameterInfo.ParameterType == typeof(int))
{
//Param is int
switch (parameterInfo.Name)
{
case "ID": //Param is an ID
toAdd = 1; //Some ID
break;
case "numberFromOneToTen": //Param is a number from 1 - 10
var random = new Random();
toAdd = random.Next(1, 10);
break;
default:
//Log?
toAdd = -9999; //e.g an error code
break;
}
}
objToPass.Add(toAdd); //Add object to pass to the list
}
method.Invoke(myClassObj, objToPass.ToArray()); //Invoke the method
}
}
}
/* Second Example */
//In this case below you are not looking for the inside of the attribute
var allMehtods = myClassObj.GetType().GetMethods()
.Where(m => m.GetCustomAttributes(typeof(myClass.myAtt), false).Length > 0)
.ToArray(); //Get all methods with the type of attribute you are looking for
foreach (var method in allMehtods)
{
method.Invoke(myClassObj, null); //Invoke method
}
}

Instantiate a reflected delegate type

I have a type variable
using System;
using System.Linq;
using System.Reflection;
...
var validateFuncType = typeof(Func<,>).MakeGenericType(someVariableType, typeof(bool));
Now I check if someVariableType follows a convention,
var validateOfType = someVariableType
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.SingleOrDefault(mi =>
{
if (mi.Name != "Validate" || mi.ReturnType != typeof(bool))
{
return false;
}
var parameters = mi.GetParameters();
return parameters.Length == 0;
});
then depending on the check
object validateFunc;
if (validateOfType == null)
{
validateFunc = // some noop func that returns true.
// e.g. _ => true;
}
else
{
validateFunc = // a delegate that calls the conventional validate
// e.g. someVariable => someVariable.Validate();
}
instantiate an instance of the delegate type.
Can you help me do that, how can I instantiate validateFuncType, that calls the conventional implementation, if it exists?
If I understand correctly, you are looking for Delegate.CreateDelegate:
var alwaysReturnTrueMethodInfo = typeof(YourClass).GetMethod("AlwaysReturnTrue").MakeGenericMethod(someVariableType);
Delegate validateFunc;
if (validateOfType == null)
{
validateFunc = Delegate.CreateDelegate(validateFuncType, alwaysReturnTrueMethodInfo);
}
else
{
validateFunc = Delegate.CreateDelegate(validateFuncType, validateOfType);
}
where AlwaysReturnTrue is a helper static method declared like this:
public static bool AlwaysReturnTrue<T>(T t) { return true }
You can do it either by:
// create an object of the type
var obj = Activator.CreateInstance(validateFuncType);
And you'll get an instance of validateFuncType in obj.
Another way is to use reflection:
// get public constructors
var ctors = validateFuncType.GetConstructors(BindingFlags.Public);
// invoke the first public constructor with no parameters.
var obj = ctors[0].Invoke(new object[] { });
This was taken from this SO answer. And because of that this question (and answer) might be marked as duplicate.
What I did, after noticing that input parameters for Func<> are contravariant.
object validateFunc = validateOfType != null
? config => (bool)validateOfType.Invoke(config, new object[0])
: new Func<object, bool>(_ => true);
I'm not sure if this is better than #Sweeper's answer

Using Type instead of specific class

I have this code:
if (((Classes.ProductGroup)o).ToString().Contains(comboBox.Text))
return true;
else
return false;
Now I want not to specify the part Classes.ProductGroup. I want to make it universal.
How can I replace the part Classes.ProductGroup with a Type object?
Type type = typeof(Classes.ProductGroup);
Something similar to this:
if (((type)o).ToString().Contains(comboBox.Text))
Is this possible?
Here is the complete method code:
private void FilterPGsinComboBox(object obj)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new FilterPGsinComboBoxDelegate(this.FilterPGsinComboBox),obj);
return;
}
Type type = ((Dictionary<ComboBox, Type>)obj).First().Value;
ComboBox comboBox = ((Dictionary<ComboBox, Type>)obj).First().Key;
comboBox.IsDropDownOpen = true;
CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(comboBox.ItemsSource);
itemsViewOriginal.Filter = ((o) =>
{
if (String.IsNullOrEmpty(comboBox.Text))
return true;
else
{
if (((Classes.ProductGroup)o).ToString().Contains(comboBox.Text))
return true;
else
return false;
}
});
itemsViewOriginal.Refresh();
}
string ToString() method is defined in the base Object class. cast to concrete type is redundant.
string filter = comboBox.Text;
itemsViewOriginal.Filter = o => String.IsNullOrEmpty(filter) ||
o != null && o.ToString().Contains(filter);
You can use generics and build a method like
public bool ClassNameContainString<T>(string text) where T : class
{
var containsString = typeof(T).ToString().Contains(text);
return containsString;
}
If you want to make this method case insensitive change the logic to
var containsString = typeof(T).ToString().ToLower().Contains(text.ToLower());

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.

How to get arguments from an Expression where TDelegate is a callback

I'm attempting to write a simple generic cache but running into problems with generating unique enough keys with using System.Func as a callback.
What I ideally want is to be able to pass in an invocable delegate of some description so that the cache itself can get the value, and determine a key all from the same expression. Right now I'm getting exceptions because I'm not passing in an argument that implements or inherits from MethodCallExpression. What should I be using instead of a System.Func for this intended behaviour?
public class SimpleCacheKeyGenerator : ICacheKey
{
public string GetCacheKey<T>(Expression<Func<T>> action)
{
var body = (MethodCallExpression) action.Body; //!!! Exception Raised - action.Body is FieldExpression
ICollection<object> parameters = (from MemberExpression expression in body.Arguments
select
((FieldInfo) expression.Member).GetValue(
((ConstantExpression) expression.Expression).Value)).ToList();
var sb = new StringBuilder(100);
sb.Append(body.Type.Namespace);
sb.Append("-");
sb.Append(body.Method.Name);
parameters.ToList().ForEach(x =>
{
sb.Append("-");
sb.Append(x);
});
return sb.ToString();
}
}
public class InMemoryCache : ICacheService
{
private readonly ICachePolicy _cachePolicy;
private readonly ICacheKey _cacheKey;
public InMemoryCache(ICachePolicy cachePolicy, ICacheKey cacheKey)
{
_cachePolicy = cachePolicy;
_cacheKey = cacheKey;
}
public T Get<T>(Func<T> getItemCallback) where T : class
{
var cacheID = _cacheKey.GetCacheKey(() => getItemCallback);
var item = HttpRuntime.Cache.Get(cacheID) as T;
if (item == null)
{
item = getItemCallback();
if (_cachePolicy.RenewLeaseOnAccess)
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
}
else
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
}
}
return item;
}
}
The problem is, you can't easily use both the Expression> and Func representing the same thing without duplicating the code.
You could possibly convert Expression> to a Func with LambdaExpression>.Compile() method, but that could create a performance problem, since Compile actually uses assembly emit, which is quite expensive.
Here is how i would implement the same thing without using Expressions and compilation.
You can find the same pattern everywhere in the standard Linq extensions.
Pass your argument as a separate object.
The type you use as an argument will be used for type inference for the delegate, and the argument itself will provide the arguments for the delegate at the same type.
Note that the cache in this implementation works because of the default ToString implementation of the anonimous objects used as arguments.
void Main()
{
var computeCount = 0;
var item1 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item1);
var item2 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item2);
var item3 = GetCached(new{x = 1, y = 3}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item3);
Console.WriteLine("Compute count:");
Console.WriteLine(computeCount);
}
Dictionary<string, object> _cache = new Dictionary<string, object>();
E GetCached<T, E>(T arg, Func<T,E> getter)
{
// Creating the cache key.
// Assuming T implements ToString correctly for cache to work.
var cacheKey = arg.ToString();
object result;
if (!_cache.TryGetValue(cacheKey, out result))
{
var newItem = getter(arg);
_cache.Add(cacheKey, newItem);
return newItem;
}
else
{
Console.WriteLine("Cache hit: {0}", cacheKey);
}
return (E)result;
}
Console output:
3
Cache hit: { x = 1, y = 2 }
3
4
Compute count:
2
You get this exception because (() => getItemCallback) means (() => { return getItemCallback; })
That's why action.Body is not a method call, it is the return statement. If you change your code to (() => getItemCallback()) you should not have the error. But you won't have any arguments.
To obtain arguments of the base call, you will have to change your code to accept an Expression and Compile your lambda.
public T Get<T>(Expression<Func<T>> getItemCallbackExpression) where T : class
{
var cacheID = _cacheKey.GetCacheKey(getItemCallbackExpression);
var item = HttpRuntime.Cache.Get(cacheID) as T;
if (item == null)
{
item = getItemCallback.Compile()();
if (_cachePolicy.RenewLeaseOnAccess)
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
}
else
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
}
}
return item;
}
I won't recommend this approach because compiling an expression takes time.
It may be easier and more performant to manually generate cache keys. If you really want to automatically manage cache keys. You may have a look to Aspect Oriented Programmation using castle.Core or PostSharp. Theses tools will allow you to automatically add code to some of your methods and automatically add cache logic.
I modified the code as below, I got the expected result this way, so you can try this, I hope this would be helpful.
public class SimpleCacheKeyGenerator
{
public string GetCacheKey<T, TObject>(Expression<Func<T, TObject>> action)
{
var body = (MethodCallExpression) action.Body;
ICollection<object> parameters = body.Arguments.Select(x => ((ConstantExpression) x).Value).ToList();
var sb = new StringBuilder(100);
sb.Append(body.Type.Namespace);
sb.Append("-");
sb.Append(body.Method.Name);
parameters.ToList().ForEach(x =>
{
sb.Append("-");
sb.Append(x);
});
return sb.ToString();
}
}
public class InMemoryCache
{
public void Get<T, TObject>(Expression<Func<T, TObject>> getItemCallback)
{
var generator = new SimpleCacheKeyGenerator();
Console.WriteLine(generator.GetCacheKey(getItemCallback));
}
}
main:
private static void Main(string[] args)
{
var cache = new InMemoryCache();
var tt = new SomeContextImpl();
cache.Get<SomeContextImpl, string>(x => x.Any("hello", "hi"));
Console.ReadKey();
}
somcontextimpl:
public class SomeContextImpl
{
public string Any(string parameter1, string parameter2) { return ""; }
}

Categories

Resources