Assembly.LoadFrom get Custom Attribute - c#

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.

Related

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.

Cannot get Method from assembly at runtime

I'm using the following code to load an assembly at runtime and then get a reference to a specific method and obviously execute it at the end:
var assemblyLoaded = Assembly.LoadFile(absolutePath);
var type = assemblyLoaded.GetType("CreateContactPlugin.Plugin");
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod("Execute", new Type[] { typeof(System.String)});
if (methodInfo == null)
{
throw new Exception("No such method exists.");
}
Here is the assembly that I'm calling
namespace CreateContactPlugin
{
public class Plugin
{
static bool Execute(string contactName){
bool contactCreated = false;
if (!String.IsNullOrWhiteSpace(contactName))
{
//process
}
return contactCreated;
}
}
}
I can succesfully load the Assembly, the Type. When I highlight the type variable, I see the method listed in the DeclaredMethods array. But when I try to get the Method, it returns always null.
Does somebody see what I might be doing wrong here ?
There's a couple of problems here. First of all the Execute method is static and not public so you need to specify the correct binding flags to get at it.
var methodInfo = type.GetMethod("Execute", BindingFlags.Static | BindingFlags.NonPublic);
However, an alternative (and preferable in my opinion) solution using less reflection and strong typing would be to make your plugin class implement a common interface, that way you can strongly type your instance object. First make a class library with the relevant interfaces in it, for example:
public interface IContactPlugin
{
bool Execute(string contactName);
}
Now your plugin can also reference the same library and becomes this:
namespace CreateContactPlugin
{
public class Plugin : IContactPlugin
{
public bool Execute(string contactName)
{
//snip
}
}
}
And your calling code would now be this:
var assemblyLoaded = Assembly.LoadFile(absolutePath);
var type = assemblyLoaded.GetType("CreateContactPlugin.Plugin");
var instance = Activator.CreateInstance(type) as IContactPlugin;
if (instance == null)
{
//That type wasn't an IContactPlugin, do something here...
}
instance.Execute("name of contact");
The problem is "static" of
static bool Execute(string contactName)
put it as
public bool Execute(string contactName)

getting the property type in a generic class (Mono.Cecil)

I am using Mono.Cecil to automatically generate (lots of, simple, generic) factory methods providing a convenient API for a library. The factories are generated for properties marked with a special custom attribute. To generate them, I must know the type of such property. The non-generic case is simple:
ModuleDefinition module = /* obtained from ReadAssembly */
foreach (var type in module.Types)
if (/* type is marked with the right attribute */)
foreach (var prop in type.Properties)
if (/* prop is marked with the right attribute */)
GenerateFactory(type, prop, prop.PropertyType);
However, some of the types marked are in fact generics. In this case, the attribute on the type contains the generic arguments for which the factory should be made, like this:
[EmitFactories(typeof(int))]
public class Class<T>
{
[MagicProperty]
T Property { get; set; }
}
(here, I want the factory to be made for Class<int>.Property).
I am handling this case by making type a GenericInstanceType. However, I cannot get to the type of the property -- to enumerate type.Properties I need to first call Resolve(), which loses all generic information. The property type is then T (instead of int), which of course makes later code fail miserably.
Mono.Cecil has GenericInstanceType and GenericInstanceMethod, but there is no equivalent for properties. I tried using module.Import(prop.PropertyType, type) (giving type as the generic parameter provider), but this doesn't work.
Do you have any ideas on how I can resolve the actual property type? Note, that it can be completely unrelated to T, T itself, or have T burried inside (e.g., List<T>). Ideally, it would work given type as a TypeReference -- this way I would not have to write separate code for the non-generic and generic cases.
Based on https://stackoverflow.com/a/16433452/613130, that is probably based on https://groups.google.com/d/msg/mono-cecil/QljtFf_eN5I/YxqLAk5lh_cJ, it should be:
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Rocks;
namespace Utilities
{
public class EmitFactoriesAttribute : Attribute
{
public readonly Type[] Types;
public EmitFactoriesAttribute()
{
}
public EmitFactoriesAttribute(params Type[] types)
{
Types = types;
}
}
public class MagicPropertyAttribute : Attribute
{
}
public static class EmitFactories
{
public static void WorkOnAssembly(string path)
{
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("ConsoleApplication4.exe");
ModuleDefinition module = assembly.MainModule;
TypeDefinition emitFactoriesAttribute = module.Import(typeof(EmitFactoriesAttribute)).Resolve();
TypeDefinition magicPropertyAttribute = module.Import(typeof(MagicPropertyAttribute)).Resolve();
foreach (TypeDefinition type in module.Types)
{
CustomAttribute emitFactory = type.CustomAttributes.SingleOrDefault(x => x.AttributeType.MetadataToken == emitFactoriesAttribute.MetadataToken);
if (emitFactory == null)
{
continue;
}
TypeReference typeRef = type;
TypeReference[] replacementTypes;
if (emitFactory.ConstructorArguments.Count != 0)
{
var temp = ((CustomAttributeArgument[])emitFactory.ConstructorArguments[0].Value);
replacementTypes = Array.ConvertAll(temp, x => (TypeReference)x.Value);
}
else
{
replacementTypes = new TypeReference[0];
}
if (replacementTypes.Length != type.GenericParameters.Count)
{
throw new NotSupportedException();
}
if (replacementTypes.Length != 0)
{
typeRef = typeRef.MakeGenericInstanceType(replacementTypes);
}
foreach (PropertyDefinition prop in type.Properties)
{
CustomAttribute magicProperty = prop.CustomAttributes.SingleOrDefault(x => x.AttributeType.MetadataToken == magicPropertyAttribute.MetadataToken);
if (magicProperty == null)
{
continue;
}
MethodReference getter = prop.GetMethod;
MethodReference setter = prop.SetMethod;
if (replacementTypes.Length != 0)
{
if (getter != null)
{
getter = getter.MakeHostInstanceGeneric(replacementTypes);
}
if (setter != null)
{
setter = setter.MakeHostInstanceGeneric(replacementTypes);
}
}
}
}
}
}
public static class TypeReferenceExtensions
{
// https://stackoverflow.com/a/16433452/613130
public static MethodReference MakeHostInstanceGeneric(this MethodReference self, params TypeReference[] arguments)
{
var reference = new MethodReference(self.Name, self.ReturnType, self.DeclaringType.MakeGenericInstanceType(arguments))
{
HasThis = self.HasThis,
ExplicitThis = self.ExplicitThis,
CallingConvention = self.CallingConvention
};
foreach (var parameter in self.Parameters)
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
foreach (var generic_parameter in self.GenericParameters)
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
return reference;
}
}
// Test
[EmitFactories(typeof(int), typeof(long))]
public class Class<TKey, TValue>
{
[MagicProperty]
Dictionary<TKey, TValue> Property1 { get; set; }
[MagicProperty]
List<TValue> Property2 { get; set; }
}
}
You hadn't defined how EmitFactoriesAttribute was, so I have written it as a EmitFactoriesAttribute(params Type[] types), to be able to accept multiple substitutions for cases like Class<TKey, TValue>.
In the end I'm not manipulating directly the property: I'm manipulating its getter and setter.
I'm not able to test it, because I don't have the factory...

Add an extra interface using Castle Dynamic Proxy 2?

I would like to create a dynamic proxy to an existing type, but add an implementation of a new interface, that isn't already declared on the target type. I can't figure out how to achieve this. Any ideas?
You can use the overload of ProxyGenerator.CreateClassProxy() that has the additionalInterfacesToProxy parameter. For example, if you had a class with a string name property and wanted to add an IEnumerable<char> to it that enumerates the name's characters, you could do it like this:
public class Foo
{
public virtual string Name { get; protected set; }
public Foo()
{
Name = "Foo";
}
}
class FooInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (invocation.Method == typeof(IEnumerable<char>).GetMethod("GetEnumerator")
|| invocation.Method == typeof(IEnumerable).GetMethod("GetEnumerator"))
invocation.ReturnValue = ((Foo)invocation.Proxy).Name.GetEnumerator();
else
invocation.Proceed();
}
}
…
var proxy = new ProxyGenerator().CreateClassProxy(
typeof(Foo), new[] { typeof(IEnumerable<char>) }, new FooInterceptor());
Console.WriteLine(((Foo)proxy).Name);
foreach (var c in ((IEnumerable<char>)proxy))
Console.WriteLine(c);
Note that the Name property doesn't have to be virtual here, if you don't want to proxy it.
use overload for creation of proxies that accepts additionalInterfacesToProxy argument

How to put an interface in a generic dynamically?

I have the following test class:
public class OutsideClass
{
private List<Type> _interfaces = null;
public void InjectInterfaces(Type[] types)
{
if(_interfaces == null)
{
_interfaces = new List<Type>();
}
foreach (var type in types)
{
if(type.IsInterface)
{
_interfaces.Add(type);
}
}
}
public void PerformSomethingWithTheInterfaces()
{
foreach (var i in _interfaces)
{
new Test<i>().PerformSomething(); // On this line the error occurs
}
}
}
internal class Test<T>
{
internal void PerformSomething()
{
}
}
This gives me on however the message Type or namespace name expected. How can I adjust this code so that it works?
What I am trying to do is to pass in a bunch of interfaces to a class library, there loop over the interfaces and use Unity to Resolve, based on the interface, something. I use the Resolve extension method.
You'd need to use reflection... something like this:
foreach (Type type in _interfaces)
{
Type concreteType = typeof(Test<>).MakeGenericType(new Type[] { type });
MethodInfo method = concreteType.GetMethod("PerformSomething",
BindingFlags.Instance | BindingFlags.NonPublic);
object instance = Activator.CreateInstance(concreteType);
method.Invoke(instance, null);
}
(You may need to make minor changes - the above isn't tested or even compiled.)
With C# 4 and dynamic typing, you can make it somewhat simpler:
foreach (Type type in _interfaces)
{
Type concreteType = typeof(Test<>).MakeGenericType(new Type[] { type });
dynamic d = Activator.CreateInstance(concreteType);
d.PerformSomething();
}
You cannot pass values as generic arguments. Only types. To be clear:
typeof(string) != string.
I don't think this can work. You specialize a generic statically on the type name. You can't pass in a reference to the Type object.
There are ways to do what you want, but they involve C# 4 and DynamicObject.
You might really want to look at C# 4's MEF just as an idea as a unity replacement I think it stands up really well myself and simplifies the resolution mechanisms a lot, and may give functionality to complete the task you're attempting more simply..
namespace MEF_Interface
{
// Interface to recognize the concrete implementation as
public interface IMessageWriter
{
void WriteMessage();
}
}
namespace MEF_HelloMessageWriter
{
// Concrete implementation in another assembly
[Export(typeof(IMessageWriter))]
public class HelloMessageWriter : IMessageWriter
{
public void WriteMessage() { Console.WriteLine("Hello!"); }
}
}
namespace MEF_GoodbyeMessageWriter
{
// Concrete implementation in another assembly
[Export(typeof(IMessageWriter))]
public class GoodbyeMessageWriter : IMessageWriter
{
public void WriteMessage() { Console.WriteLine("Goodbye!"); }
}
}
namespace MEF_Example
{
class DIContainer
{
[Import]
public IMessageWriter MessageWriter { get; set; }
public DIContainer(string directory)
{
// No more messy XML DI definition, just a catalog that loads
// all exports in a specified directory. Filtering is also available.
DirectoryCatalog catalog = new DirectoryCatalog(directory);
catalog.Refresh();
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
class Program
{
static void Main(string[] args)
{
string helloMessageWriterPath =
#"C:\shared\Projects\MEF_Example\MEF_HelloMessageWriter\bin\Debug";
string goodbyeMessageWriterPath =
#"C:\shared\Projects\MEF_Example\MEF_GoodbyeMessageWriter\bin\Debug";
DIContainer diHelloContainer = new DIContainer(helloMessageWriterPath);
diHelloContainer.MessageWriter.WriteMessage();
DIContainer diGoodbyeContainer = new DIContainer(goodbyeMessageWriterPath);
diGoodbyeContainer.MessageWriter.WriteMessage();
Console.ReadLine();
}
}
}

Categories

Resources