Find out if class implements an interface thru reflection - c#

I have the following class in c#:
public class WorkOrderStatus : ICustomEnum<WorkOrderStatus>
{
}
during runtime I need to figure out if a property of the following class implements the custom interface.
public class WorkOrder{
public WorkOrderStatus Status {get;set;}
}
So I tried to do the following(using reflection to figure it out):
prop.PropertyType.GetInterfaces().Contains(typeof(ICustomEnum));
But this says that ICustomEnum requires a generic type. So i Tried to do the following, but it doesnt work:
var type = prop.GetType();
prop.PropertyType.GetInterfaces().Contains(typeof(ICustomEnum<typeof(type)>));
saying type is a variable but used like a type
[Edit 1]
I later need to be able to create an instance of WorkOrderStatus or any other class that implements this interface thru reflection like this:
var instance = (ICustomEnum<WorkOrderStatus|SomeOtherStatus...>)Activator.CreateInstance(prop.PropertyType);

It's quite simple to do with IsAssignableTo:
Type propertyType = typeof(WorkOrder).GetProperty("Status").PropertyType;
Type interfaceType = typeof(ICustomEnum<>).MakeGenericType(propertyType);
bool implements = propertyType.IsAssignableTo(interfaceType);
And, you might find that typeof(ICustomEnum<>).MakeGenericType(propertyType); solves your problem in your code directly.
So instead of typeof(ICustomEnum<typeof(type)>) you write typeof(ICustomEnum<>).MakeGenericType(type).
Here's how you would use reflection to call a method so that you can move from run-time types back to compile-time types.
void Main()
{
Type propertyType = typeof(WorkOrder).GetProperty("Status").PropertyType;
Type interfaceType = typeof(ICustomEnum<>).MakeGenericType(propertyType);
bool implements = propertyType.IsAssignableTo(interfaceType);
if (implements)
{
object propertyInstance = Activator.CreateInstance(propertyType);
var method =
this
.GetType()
.GetMethod("SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(propertyType);
method.Invoke(this, new[] { propertyInstance });
}
}
private void SomeMethod<T>(ICustomEnum<T> customEnum)
{
Console.WriteLine($"Success with {typeof(T)}");
}
That outputs the following:
Success with WorkOrderStatus
Here's the sundry code you need to run the above:
public class WorkOrderStatus : ICustomEnum<WorkOrderStatus> { }
public interface ICustomEnum<T> { }
public class WorkOrder
{
public WorkOrderStatus Status { get; set; }
}

You could use GetGenericTypeDefinition for this purpose. For example,
propertyInfo.PropertyType
.GetInterfaces()
.Where(x => x.IsGenericType)
.Any(i => i.GetGenericTypeDefinition() == typeof(ICustomEnum<>));
To retrieve all properties of the Type which implements the particular generic interface, you could
var properties = typeof(WorkOrder).GetProperties();
var result = properties.Where(property=>property.PropertyType
.GetInterfaces()
.Where(x => x.IsGenericType)
.Any(i => i.GetGenericTypeDefinition() == typeof(ICustomEnum<>)));

Related

How to call non-generic methods inside generic types using reflection

Using .Net Framework 4.8.
I'm creating a shortcut system for my MDI WinForms application, so you can invoke methods when you press certain keys on certain forms, using custom attributes.
For context, the attributes look like this, and save them as Shortcutentry:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class ShortcutMethodAttribute : Attribute
{
public Keys[] Combination {get; set;}
public ShortcutMethodAttribute(params Keys[] combination)
{
Combination = combination;
}
}
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
public sealed class ShortcutTypeAttribute : Attribute
{
}
public class ShortcutEntry
{
public ShortcutMethodAttribute Attribute { get; private set; }
public object Object { get; set; }
public Keys[] KeyCombination { get; set; }
public MethodInfo MethodInfo { get; private set; }
public ShortcutEntry(object #object, Keys[] keyCombination, MethodInfo methodInfo, ShortcutMethodAttribute attrib)
{
this.Object = #object;
this.KeyCombination = keyCombination;
this.MethodInfo = methodInfo;
this.Attribute = attrib;
}
public void Trigger()
{
MethodInfo.Invoke(Object, null);
}
}
I resolve all shortcuts like this and save them as a Dictionary<Type, ShortcutEntry>:
public Dictionary<Type, List<ShortcutEntry>> RegisterAllAssemblyShortcuts()
{
var shortcuts = new Dictionary<Type, ShortcutEntry>();
var types = Assembly.GetExecutingAssembly().GetTypes();
var typesWithAttribute = types.Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any());
foreach (var type in typesWithAttribute)
{
var methods = type.GetMethods().Where(x => x.GetCustomAttributes(typeof(ShortcutMethodAttribute), false).Length > 0);
foreach (var method in methods)
{
var attributes = method.GetCustomAttributes(typeof(ShortcutMethodAttribute), false).OfType<ShortcutMethodAttribute>();
if (attributes == null) continue;
foreach (var attribute in attributes)
{
var se = new ShortcutEntry(
null,
attribute.KeyCombination,
method,
attribute
);
if (!shortcuts.ContainsKey(type)) shortcuts.Add(type, new List<ShortcutEntry>);
shortcuts[type].Add(se);
}
}
}
return shortcuts;
}
To use it, you need to assign the ShortcutTypeAttribute to a type, and then ShortcutMethodAttribute to the method you want to call, with the key combination passed as parameter.
[ShortcutTypeAttribute]
public class SomeClass
{
public void SomeMethodA()
{
// do something
}
[ShortcutMethodAttribute(Keys.O, keys.I)]
public void SomeMethodB()
{
// do something
}
}
To summarize, it works like this:
Add ShortcutTypeAttribute to type containing the methods you want to call.
Add ShortcutMethodAttribute to the method to be called (with key combination).
Call RegisterAllAssemblyShortcuts()
Determine the type of the active MDI form.
Listen for keyboard input and check if shortcuts[mdiType] has any match.
If there is a ShortcutEntry then assing the Object and call ShortcutEntry.Trigger().
All of this steps work fine
The problem arises when I try to call a non-generic method with ShortcutEntry.Trigger() that is declared on a generic type, like so:
[ShortcutTypeAttribute]
public class KeyboundForm<T> : Form where T : class
{
[ShortcutMethodAttribute(Keys.O)]
public virtual void KeyOPressed() {}
}
The exception I get is:
System.InvalidOperationException: 'Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.'
I don't know why the MethodInfo for KeyOPressed() has MethodInfo.ContainsGenericParameters = true, when:
MethodInfo.IsGenericMethod = false
MethodInfo.IsGenericMethodDefinition = false
So I can't call MakeGenericMethod() on the KeyOPressed's MethodInfo
How can I invoke a non-generic method in a generic type?
Answer Edit: now it's working
Replaced the Trigger Function to recalculate methodinfo when it was generic.
public void Trigger()
{
if (MethodInfo.ContainsGenericParameters)
{
var type = Object.GetType();
var methodinfo = type.GetMethod(MethodInfo.Name);
methodinfo.Invoke(Object, null);
}
else
{
MethodInfo.Invoke(Object, null);
}
}
I don't know why the MethodInfo for KeyOPressed() has MethodInfo.ContainsGenericParameters == true`, when ...
This is because KeyOPressed is declared in a generic type. You need to create bound generic type (i.e. KeyboundForm<SomeActualForm>) to be able to invoke it.
One approach is to change your reflection to support only bound generic types:
var typesWithAttribute = types
.Where(t => !t.ContainsGenericParameters)
.Where(x => x.GetCustomAttributes<ShortcutTypeAttribute>(false).Any())
Which will capture non-generic types like SomeClass and bound generic types like SomeOtherClass : KeyboundForm<SomeFormType> marked with corresponding attribute.
Or check for inherited attributes (GetCustomAttributes<ShortcutTypeAttribute>(true)) for classes which are bound generic types (Type.IsConstructedGenericType == true).
Related:
Invoke a non generic method with generic arguments defined in a generic class

Read non-static property value of a subclass via Reflection

I am implementing a variation of the Command design pattern. I have an abstract CodedCommand class with some subclasses, each of one with an overriden "Code" property of type char. Each subclass has then the responsibility to "encapsulate" its own Code letter to which it is associated.
The use case is: when some serial parser receive a char/byte, I want to create an instance of the appropriate subclass, to further process the command payload.
My problem is: I don't know how to get the specific value from each subclass.
My current implementation is below. The problem, highlighted with a comment, is that type.GetProperty("Code") is returning null!
internal abstract class CodedCommand
{
internal abstract char Code { get; }
}
internal class EventA : CodedCommand
{
internal override char Code => 'A';
}
internal class EventB : CodedCommand
{
internal override char Code => 'B';
}
public class CommandCreator
{
Dictionary<char, Type> _typeMap
= new Dictionary<char, Type>();
public CommandCreator()
{
var types = GetType().Assembly.GetTypes()
.Where(type => type.IsSubclassOf(typeof(CodedCommand)))
.Where(type => !type.IsAbstract);
foreach (var type in types)
{
var field = type.GetProperty("Code"); // returns null!!!
var value = field.GetValue(null);
var code = (char)value;
_typeMap[code] = type;
}
}
CodedCommand BuildCommand(char code)
{
_typeMap.TryGetValue(code, out Type type);
if (type != null)
{
return (CodedCommand)(Activator.CreateInstance(type));
}
return null;
}
}
So my question is how can I fix this design?
Here is complete solution for getting value of property:
var types = GetType().Assembly.GetTypes()
.Where(type => type.IsSubclassOf(typeof(CodedCommand)) && !type.IsAbstract);
foreach (var type in types)
{
var obj = Activator.CreateInstance(type);
var field = type.GetProperty("Code", BindingFlags.Instance | BindingFlags.NonPublic);
var value = field.GetValue(obj);
var code = (char)value;
_typeMap[code] = type;
}
Please note that field.GetValue needs object instance to work and NonPublic-BindingFlag is needed when getting PropertyInformation for internal property.
The problem is your property is marked as internal, not public and we cannot access the non-public properties using the overload which you are using.
You need to specify the flags parmeter by using this overload if you want properties other than public like:
type.GetProperty("Code",BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public)
GetProperty(String) overload:
Searches for the public property with the specified name.
GetProperty(string name,BindingFlags bindingAttr) overload:
Searches for the specified property, using the specified binding constraints.

Use Reflection in C# to get a constructor that accepts a type or a derived type

I'd like to use reflection on a certain type argument T to get its constructors.
The constructors i'd like to get are ones that accept certain Type ISomeType, or any type derived from it.
For example:
public interface ISomeType
{
}
public class SomeClass : ISomeType
{
}
I'd like to find constructors that either accept ISomeType, SomeClass, or any other ISomeType derived class.
Is there any easy way of achieving this?
You could do something like this:
public List<ConstructorInfo> GetConstructors(Type type, Type baseParameterType)
{
List<ConstructorInfo> result = new List<ConstructorInfo>();
foreach (ConstructorInfo ci in type.GetConstructors())
{
var parameters = ci.GetParameters();
if (parameters.Length != 1)
continue;
ParameterInfo pi = parameters.First();
if (!baseParameterType.IsAssignableFrom(pi.ParameterType))
continue;
result.Add(ci);
}
return result;
}
which is equivalent with
public IEnumerable<ConstructorInfo> GetConstructors(Type type, Type baseParameterType)
{
return type.GetConstructors()
.Where(ci => ci.GetParameters().Length == 1)
.Where(ci => baseParameterType.IsAssignableFrom(ci.GetParameters().First().ParameterType)
}
when you add some LINQ magic
You can do it like this:
Type myType = ...
var constrs = myType
.GetConstructors()
.Where(c => c.GetParameters().Count()==1
&& c.GetParameters()[0].ParameterType.GetInterfaces().Contains(typeof(ISomeType))
).ToList();
if (constrs.Count == 0) {
// No constructors taking a class implementing ISomeType
} else if (constrs.Count == 1) {
// A single constructor taking a class implementing ISomeType
} else {
// Multiple constructors - you may need to go through them to decide
// which one you would prefer to use.
}
base class doesn't know about own devived classes, that's why you can't get derived class's ctors. you must get all classes from assembly and find accept ctors among them

Casting generic type instances created using Reflection

I'm creating instances of a generic type using reflection:
public interface IModelBuilder<TModel>
{
TModel BuildModel();
}
public class MyModel
{
public string Name { get; set; }
}
public class MyModelBuilder : IModelBuilder<MyModel>
{
public MyModel BuildModel()
{
throw new NotImplementedException();
}
}
At runtime all we know is the Type of model e.g. MyModel. I can find instances of the relevant model builder like so:
var modelBuilders = from t in Assembly.GetExecutingAssembly().GetTypes()
from i in t.GetInterfaces()
where i.IsGenericType
&& i.GetGenericTypeDefinition() == typeof(IModelBuilder<>)
&& i.GetGenericArguments()[0] == modelType
select t;
var builder = Activator.CreateInstance(modelBuilders.First());
But I'm not sure how I can then cast the instance as IModelBuilder<TModel> so I can call and work with the result of BuildModel().
Since modelType is just a Type instance, you can't do that automatically, since there is no non-generic API available. Various options:
1: use reflection, for example (untested)
object builder = Activator.CreateInstance(...);
var model=builder.GetType().GetMethod("BuildModel").Invoke(builder,null);
2: cheat with dynamic:
dynamic builder = Activator.CreateInstance(...);
var model = builder.BuildModel();
3: make a non-generic version of IModelBuilder, and use that
Note that 1 & 2 rely on a public implementation of the interface, and will fail for a (perfectly legal) explicit interface implementation. For "1", you can fix this via:
var model = typeof(IModelBuilder<>).MakeGenericType(modelType)
.GetMethod("BuildModel").Invoke(builder);
A final sneaky option is to flip from a non-generic method into a generic method, so inside the generic method you can use all the members directly. There's a lazy way to do that via dynamic:
interface ISneaky<T>
{
T Foo { get; }
}
class Sneaky<T> : ISneaky<T>
{
T ISneaky<T>.Foo { get { return default(T); } }
}
class Program
{
static void Main()
{
Execute(typeof(int));
}
static void Execute(Type t)
{
dynamic obj = Activator.CreateInstance(
typeof(Sneaky<>).MakeGenericType(t));
// crafy hack to flip from non-generic code into generic code:
Evil(obj);
}
static void Evil<T>(ISneaky<T> sneaky)
{ // in here, life is simple; no more reflection
Console.WriteLine("{0}: {1}", typeof(T).Name, sneaky.Foo);
}
}

Is there a way to deal with unknown generic types?

I have this code
public interface IConsumable<T> {
void Consume(T item);
}
public interface IProducer<T> {
IConsumable<T> Consumer { get; set; }
void Produce();
}
public class MyClass : MyType,
IConsumable<ISpecifcItem>
{
public void Consume(ISpecificItem item) { ... }
}
public class MySpecificItemProducer
: IProducer<ISpecificItem> {
public IConsumable<ISpecificItem> Consumer { get; set; }
public void Produce() {
ISpecificItem myItem = new MyVerySpecificItem();
Consumer.Consume(myItem);
}
}
Then I'm having a controller that takes any MyType, discovers all types of IConsumable<> that it implements and gets the type of the generic type parameter. With this list of types it discovers all producers that implement IProducer<TParam>. That's not difficult:
var consumerTypes =
myType.GetType().GetInterfaces()
.Where(x => x.IsGenericType)
.Where(x => x.GetGenericTypeDefinition() ==
typeof(IConsumable<>));
if (consumerTypes.Any()) {
var instanceTypes = consumerTypes
.Select(x => x.GetGenericArguments().First())
.Select(x => typeof(IProducer<>).MakeGenericType(x));
// for each of those types discover classes where
// it assignable from
// and instantiate the class using the Activator
}
But the problem is, how do I set the Consumer property of the producer? The producer instance is an object to me, I can't cast it to an IProducer<T>, because I can't use T like a variable.
I can do it with reflection producerInstance.GetType().GetProperty("Consumer").SetValue(producerInstance, consumerInstance, null); but I wanted to know if there's another way?
Interestingly, this failed at runtime:
MyClass consumerInstance;
dynamic test = producerInstance;
test.Consumer = consumerInstance;
It complained that the type of consumerInstance was incompatible to the type of the property.
EDIT: The dynamic example worked only when consumerInstance was also a dynamic, e.g.:
dynamic testC = consumerInstance;
dynamic testP = producerInstance;
testP.Consumer = testC;
Unfortunately, without refactoring the code you provided, you cannot solve the problem without more reflection (as you have done). However, you could use reflection before you set the consumer property if it makes it more readable for you.
var method = GetType().GetMethod("Process");
var genericType = interfaceType.GetGenericArguments().First();
var invocable = method.MakeGenericMethod(genericType);
invocable.Invoke(this, new object[] { producer, consumer });
public void Process<T>(IProducer<T> producer, IConsumable<T> consumer)
{
producer.Consumer = consumer;
}
Are you giving >1 IConsumable to MyType and just altering the generic type argument? I assume you are because you get a list of those interfaces. I don't know where you get your producers from, but the only way to not use reflection is to stay out of it. You could consider forcing each 'MyType' to provide a method that would 'setup' a list of producers (MyType internally would know all of it's own consumable types). Depending on where you pull the producers from (internal to MyType or external) you may have to do the following:
public interface IProducer { }
public interface IProducer<T> : IProducer
{
IConsumable<T> Consumer { get; set; }
void Produce();
}
public interface IConsumableProvider
{
void SetupProducers(params IProducer[] producers);
}
public class MyType :
IConsumable<int>,
IConsumable<double>,
IConsumableProvider
{
public void Consume(int item)
{
throw new NotImplementedException();
}
public void Consume(double item)
{
throw new NotImplementedException();
}
public void SetupProducers(params IProducer[] producers)
{
(producers[0] as IProducer<int>).Consumer = (this as IConsumable<int>);
(producers[1] as IProducer<double>).Consumer = (this as IConsumable<double>);
}
}
I'm not in love with the solution, but I feel an optimal solution would require more information about your current code base - else I would give an answer too divergent to what you already have.

Categories