Capture Attribute's target - c#

I have following attribute definition:
[System.AttributeUsage(System.AttributeTargets.Method)]
public class ClientFunction : System.Attribute {
public static List<???> Targets = new List<???>();
public string Display;
public string Tooltip;
public ClientFunction (string display, string tooltip = null) {
Display = display;
Tooltip = tooltip;
// Add target method of this specific attribute to Targets
Targets.Add(???);
}
}
I want to add to Targets the method the attribute is assigned to. How can I do this? Maybe with a third parameter that expects a delegate or something else?

You can't do that, attributes have no knowledge of the types/methods/etc they're assigned to.
Also, attributes constructors cannot have delegates as parameters. An attribute's parameters must be compile-time constants (e.g, strings, ints, enums). See available types: http://msdn.microsoft.com/en-us/library/aa664615%28v=vs.71%29.aspx
If you want to find all methods decorated with an attribute, you'll have to use reflection and inspect every method in every type in a given assembly, for example, and check whether those methods have the attribute defined.
Also note that, by convention, all attributes' names should be suffixed with the word "Attribute". In your case, ClientFunctionAttribute.

I think what you want to do here is this:
[System.AttributeUsage(System.AttributeTargets.Method)]
public class ClientFunctionAttribute : System.Attribute
{
private static List<MethodInfo> _targets;
public static List<MethodInfo> Targets
{
get
{
if (_targets == null)
{
_targets = Assembly.GetExecutingAssembly().GetTypes()
.SelectMany(t => t.GetMethods())
.Where(m => m.GetCustomAttributes(typeof(ClientFunctionAttribute), false).Length > 0)
.ToList();
}
return _targets;
}
}
public string Display;
public string Tooltip;
public ClientFunctionAttribute(string display, string tooltip = null)
{
Display = display;
Tooltip = tooltip;
}
}
And please add Attribute postfix after class name, you can still omit it in sqare brackets, like [ClientFunctionAttribute] and [ClientFunction] are the same.

Related

C# How to set Property with two arguments

I need to setup a Property with two arguments, for example, to append text in a log file.
Example:
public string LogText(string text, bool Overwrite)
{
get
{
return ProgramLogText;
}
set
{
ProgramLogText = value;
}
}
How do I do this?
(in the example above, I need to pass the text I want to be written in the file and 1 to overwrite (0 as default value for appending the text), else append to a text file, but when I get, I just need the text.)
You can extract class - implement your own class (struct) with Text and Overwrite properties and add some syntax sugar:
public struct MyLogText {
public MyLogText(string text, bool overwrite) {
//TODO: you may want to validate the text
Text = text;
Overwrite = overwrite;
}
public string Text {get;}
public bool Overwrite {get;}
// Let's add some syntax sugar: tuples
public MyLogText((string, bool) tuple)
: this(tuple.Item1, tuple.Item2) { }
public void Deconstruct(out string text, out bool overwrite) {
text = Text;
overwrite = Overwrite;
}
public static implicit operator MyLogText((string, bool) tuple) => new MyLogText(tuple);
//TODO: You may want to add ToString(), Equals, GetHashcode etc. methods
}
And now you can put an easy syntax
public class MyClass {
...
public MyLogText LogText {
get;
set;
}
...
}
And easy assignment (as if we have a property with 2 values):
MyClass demo = new MyClass();
// Set two values in one go
demo.LogText = ("some text", true);
// Get two values in one go
(string text, bool overWrite) = demo.LogText;
You can't.
However, you have a few possible alternative approaches: create a method, or use a Tuple instead or create a class/struct and pass as a parameter (which has been answered by someone else).
Below are some alternative methods that can also be used instead.
Alternative Method 1
Create a Tuple, but then you'd have to return a tuple string, bool.
public Tuple<string, bool> LogText { get; set; }
I wouldn't do this method because then your getter would also return two values.
Alternative Method 2
Create getter and setter methods instead.
public string GetLogText() => ProgramLogText;
public void SetLogText(string text, bool overwrite) => ProgramLogText = text; // and implement in this method your use of overwrite.

C# Attribute check is an value equals the constructor argument and get constructor values

How can i check that some string is equal to the "constructor" arguments of an attribute?
And how to get all constructor values (TestArg1, TestArg2)?
struct MyData
{
[MyAttr("TestArg1", "TestArg2")] //check that some string equals TestArg1/TestArg2
public string TestArg;
}
This primarily depends on what attribute you're looking at and how it's coded. See the code below as an example on how to do what you're asking.
//The attribute we're looking at
public class MyAtt : System.Attribute
{
public string name;
public string anotherstring;
public MyAtt(string name, string anotherstring)
{
this.name = name;
this.anotherstring = anotherstring;
}
}
public static class Usage
{
[MyAtt("String1", "String2")] //Using the attribute
public static string SomeProperty = "String1";
}
public static class Program
{
public static void Main()
{
Console.WriteLine(IsEqualToAttribute("String1"));
Console.WriteLine(IsEqualToAttribute("blah"));
Console.ReadKey();
}
public static bool IsEqualToAttribute(string mystring)
{
//Let's get all the properties from Usage
PropertyInfo[] props = typeof(Usage).GetProperties();
foreach (var prop in props)
{
//Let's make sure we have the right property
if (prop.Name == "SomeProperty")
{
//Get the attributes from the property
var attrs = prop.GetCustomAttributes();
//Select just the attribute named "MyAtt"
var attr = attrs.SingleOrDefault(x => x.GetType().Name == "MyAtt");
MyAtt myAttribute = attr as MyAtt; //Just casting to the correct type
if (myAttribute.name == mystring) //Compare the strings
return true;
if (myAttribute.anotherstring == mystring) //Compare the strings
return true;
}
}
return false;
}
}
As you can see we get the attribute off the property using reflection and then just compare the properties.
More info can be found here:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/accessing-attributes-by-using-reflection
As far as getting the constructor properties something along the lines of
typeof(MyAtt).GetConstructor().GetParameters()
Would retrieve the parameter details for the constructor.
There is also info on this in the Microsoft Docs: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata.constructor?view=netframework-4.7.2
Here is one way to do what you are asking for, but it's not particularly scaleable and requires a bunch of manual code to get working but may get you on the road to what you are trying to achieve. Assuming we have an attribute something like this that takes a string array in it's constructor:
public class MyAttrAttribute : Attribute
{
public string[] AllowedValues { get; }
public MyAttrAttribute(params string[] values)
{
AllowedValues = values;
}
}
You can change your field to be a property with a backing field. This allows you to override the set method and do your checking in there:
private string _testArg;
[MyAttr("TestArg1", "TestArg2")] //check that some string equals TestArg1/TestArg2
public string TestArg
{
get => _testArg;
set
{
var allowedValues = this.GetType() //Get the type of 'this'
.GetProperty(nameof(TestArg)) // Get this property
.GetCustomAttribute<MyAttrAttribute>() // Get the attribute
.AllowedValues; //Get the allowed values specified in the attribute
if(!allowedValues.Contains(value))
{
throw new ArgumentOutOfRangeException(nameof(value),
$"The value '{value}' is not allowed");
}
_testArg = value;
}
}
Having said all of this, I firmly believe that there is a better way to achieve what you are asking. For example, if you are restricted to a minimal set of values, then an enum would almost certainly be a better option than a string.

Initiation values for runtime and reflection

For a hobby project, I'am trying to solve the following problem:
I try to get the initialisation values with which a class instance is made also available in reflection. Currently I only know how to do this by using an attribute for reflection and the new DoubleParameter(...) to set the values runtime:
[MyDoubleSettings(-10,10,5)]
public DoubleParameter param2 = new DoubleParameter(-10,10,5);
I would like to know if there is some way to only have these values in the code once. This construct will be used many times and someone is bound to only change one or the other.
I couldn't find a way to use reflection to see the new DoubleParameter(...) values. Also I couldn't find a way to see the attribute from the DoubleParameter class.
Is there any way to do this or is there some other neat way to solve this?
Kind regards,
Ernst.
You don't need an attribute.If all you want to do is to get values of some fields or properties you can do that easily using Reflection, for example if you have three public property that holds these values you can use the following:
var values = typeof(DoubleParameter)
.GetProperties()
.Select(x => x.GetValue(yourInstance))
.ToArray();
You can also cast result of GetValue to double if you sure that the type of your properties is double or filter them based on property type:
var values = typeof(DoubleParameter)
.GetProperties()
.Where(p => p.PropertyType == typeof(double))
.Select(x => (double)x.GetValue(yourInstance))
.ToArray();
To answer your question about how to get the arguments passed inside an attribute constructor:
(Also I couldn't find a way to see the attribute from the DoubleParameter class)
class Program
{
static void Main(string[] args)
{
TestClass _testClass = new TestClass();
Type _testClassType = _testClass.GetType();
// Get attributes:
// Check if the instance class has any attributes
if (_testClassType.CustomAttributes.Count() > 0)
{
// Check the attribute which matches TestClass
CustomAttributeData _customAttribute = _testClassType.CustomAttributes.SingleOrDefault(a => a.AttributeType == typeof(TestAttribute));
if (_customAttribute != null)
{
// Loop through all constructor arguments
foreach (var _argument in _customAttribute.ConstructorArguments)
{
// value will now hold the value of the desired agrument
var value = _argument.Value;
// To get the original type:
//var value = Convert.ChangeType(_argument.Value, _argument.ArgumentType);
}
}
}
Console.ReadLine();
}
}
[TestAttribute("test")]
public class TestClass
{
}
public class TestAttribute : System.Attribute
{
public TestAttribute(string _test)
{
}
}

Assembly.LoadFrom get Custom Attribute

Question: using only Assembly.LoadFrom and having only the name of a custom attribute how can I find and then instantiate any classes with that named custom attribute?
Code in DLL:
[AttributeUsage(AttributeTargets.All)]
public class ClassAttribute : Attribute
{
private string myName;
public ClassAttribute() { }
public ClassAttribute(string className)
{
myName = className;
}
public string MyName { get { return myName; } }
}
//Edit ... added this after initial post
[AttributeUsage(AttributeTargets.All)]
public class MethodAttribute : Attribute
{
private string myMethodName;
public MethodAttribute(){}
public MethodAttribute(string methodName)
{
myMethodName = methodName;
}
public string MyMethodName { get { return myMethodName; } }
}
[ClassAttribute("The First Class")]
public class myClass
{
//Edit ... added this after initial post
[MethodAttribute("Find this method after finding this class")]
public string methodOne()
{
return "This response from myclass methodOne";
}
public string methodTwo()
{
return "This response from myClass methodTwo";
}
}
Code in consuming class in separate VS2k12 solution:
Assembly asm = Assembly.LoadFrom(#"C:\References\WebDemoAttributes.dll");
string attributeName = "Find this class using this attribute name";
//using attributeName how can I find WebDemoAttributes.myClass, instantiate it, and then call
methodOne()?
Thank you in advance and cheers!
This is what I finally came up with for anyone interested in searching for classes in a DLL by custom attribute:
protected void lb_debugInClassLibrary_Click(object sender, EventArgs e)
{
LinkButton lb = sender as LinkButton;
Assembly asm1 = Assembly.Load("WebDemoAttributes");
var classAttributesTypes = asm1.GetTypes().Where(t => t.GetCustomAttributes()
.Any(a => a.GetType().Name == "ClassAttribute")).ToList();
foreach (Type type in classAttributesTypes)
{
Attribute[] attrs = Attribute.GetCustomAttributes(type);
foreach (Attribute atr in attrs)
{
var classWithCustomAttribute = atr as WebDemoAttributes.ClassAttribute;
if (classWithCustomAttribute.MyName == "The First Class"
&& lb.ID.ToString().ToLower().Contains("thefirstclass"))
{
var mc = Activator.CreateInstance(type) as WebDemoAttributes.MyClass;
//TODO figure out how to get the attributes decorating mc's methods
if (lb.ID.ToString().ToLower().Contains("methodone"))
lbl_responseFromMyClass.Text = mc.MethodOne();
else if (lb.ID.ToString().ToLower().Contains("methodtwo"))
lbl_responseFromMyClass.Text = mc.MethodTwo();
}
if (classWithCustomAttribute.MyName == "The Second Class"
&& lb.ID.ToString().ToLower().Contains("thesecondclass"))
{
var yc = Activator.CreateInstance(type) as WebDemoAttributes.YourClass;
if (lb.ID.ToString().ToLower().Contains("methodone"))
lbl_responseFromYourClass.Text = yc.MethodOne();
else if (lb.ID.ToString().ToLower().Contains("methodtwo"))
lbl_responseFromYourClass.Text = yc.MethodTwo();
}
}
}
}
var asm = Assembly.LoadFrom(#"C:\References\WebDemoAttributes.dll");
var myClassType = asm.GetTypes()
.FirstOrDefault(t => t.GetCustomAttributes()
.Any(a => a.GetType().Name == "ClassAttribute"));
Your question:
using attributeName how can I find WebDemoAttributes.myClass, instantiate it, and then call methodOne()?
The answer:
Don't use attributes. Use interfaces instead.
Attributes are used to set compile-time properties associated with classes that are accessible via reflection. They can be used to mark classes, methods, and assemblies, such that you can search for those specific items based on the existence of the attribute.
They cannot be used to enforce design constraints (at least, not out of the box). So, asking if you can locate a given class via attribute search, and then invoke methodOne() on the class, is not a valid question, because the existence of the attribute on the class does not imply the existence of methodOne.
Instead, I would suggest the use of an interface. You can find all classes that implement an interface, and invoke the method using reflection. This post gives a simple overview of that.

How can I implement custom attributes in .NET 2.0?

Unfortunately I am still working on .NET 2.0. I have not created a custom attribute before.
I want to create a CustomStringFormatAttribute:.
If a class, say Customer.Name, has:
MaxLength=30
ActualLength=10
I need to pad it with empty spaces till it reached 30.
I also need an attribute for date that I can format like DisplayDataFormat
I have created the following but How do I get access to the actual value of the property within the attribute?
public class Customer
{
[CustomStringFormatAttribute(30)]
public string Name { get; set; }
//todo:customDateAttribute
public DateTime StartDate { get; set; }
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public sealed class CustomStringFormatAttribute : Attribute
{
private readonly int maxLength;
public CustomStringFormatAttribute(int maxLength)
{
MaxLength = maxLength;
}
public int MaxLength { get; private set; }
//?Should I override ToString
public override string ToString()
{
return Format();
}
private string Format()
{
//simplified version of my formatting for brevity
string source = "value from the property of the class.";//How do I get access to the actual value of the property within the attribute?
const char paddingChar = ' ';
return source.PadLeft(maxLength, paddingChar);
}
}
Any suggestions?
Note: I' used automatic property for brevity. I don't have that luxury in .NET 2.0.
Sorry, you cannot access the class instance or the property info inside your attribute.
You should write an additional method, for example a static method in some "static" class, that allow you to do what you want to do.
Example....
public static string FormatProperty(object instance, PropertyInfo property)
{
CustomStringFormatAttribute attrib = Attribute.GetCustomAttribute(property, typeof(CustomStringFormatAttribute)) as CustomStringFormatAttribute;
return property.GetValue(instance, null).ToString().PadLeft(attrib.MaxLength, ' ');
}
public static string FormatProperty(object instance, string propertyName)
{
return FormatProperty(instance, instance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance));
}
But this is very uncomfortable and insanely slow since uses reflection to get property value through property info.
To access property attributes you need a PropertyInfo.
public static int GetPropertyMaxLength(PropertyInfo property)
{
CustomStringFormatAttribute attrib = Attribute.GetCustomAttribute(property, typeof(CustomStringFormatAttribute)) as CustomStringFormatAttribute;
return attrib != null ? attrib.MaxLength : int.MaxValue;
}
public static int GetPropertyMaxLength(Type type, string propertyName)
{
return GetPropertyMaxLength(type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance));
}
Let's suppose we put these functions inside the attribute.
Then we want to override, for example, our ToString method in our class Customer.
public override string ToString()
{
return CustomStringFormatAttribute.FormatProperty(this, "Name");
}
The problem with this is of course Speed, it uses reflection by name, very slow, and Refactoring, you will not have a compile time warning or error if property "Name" doesn't exists, you will get only an exception at runtime. I would suggest you to use another mechanism.
With newer version of the language you could use lambda expressions to obtain your property info directly by the property itself, but since you are in C# 2.0 this is not possible.
Another solution can be: add instead another property called FormattedXXX, for example FormattedName that returns the lenght as you want it and you can use that property instead of the Name property.
This will allow you to keep your formatted version of the property near your property.
You need to do it the other way around. You shouldn't have any logic in your attribute, it should simply expose properties with the information it contains (e.g. a MaxLength property). Then your Customer class should access the information provided by CustomStringFormatAttribute and format it accordingly:
private string m_Name;
public string Name
{
get
{
var formatAttribute = typeof(Customer).GetCustomAttributes(false)
.OfType<CustomStringFormatAttribute>
.SingleOrDefault();
if (formatAttribute != null)
return m_Name.PadLeft(formatAttribute.MaxLength);
return m_Name;
}
set
{
m_Name = value;
}
}

Categories

Resources