Is there a way to deal with unknown generic types? - c#

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.

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

Best way to handle creation of large number of subtype objects

I have a base Message class, and around 100 different subtype classes of Message that represent each type of message that can be processed. What I am currently considering doing is using a giant switch statement to create the message object. For example:
switch (MsgType)
{
case MessageType.ChatMsg:
Msg = new MsgChat(Buf);
break;
case MessageType.ResultMsg:
Msg = new MsgResult(Buf);
break;
... // 98 more case statements
}
Msg.ProcessMsg(); // Use a polymorphic call to process the message.
Is there a better way to do this? If so, can you show an easy code example.
EDIT
So, I tried doing this:
public class Test
{
public Test()
{
IEnumerable<Type> myEnumerable = GetTypesWith<MyAttribute>(true);
}
IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit)
where TAttribute : System.Attribute
{
return from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
where t.IsDefined(typeof(TAttribute), inherit)
select t;
}
}
This appears to work in that myEnumerable now contains all 100 of the message subtypes, plus the base Message type as well. However, while I don't mind using reflection at the beginning of the program to load the types, using it to access the proper object in real time might be too slow. So, I would like to try out using a delegate.
The example in the comment below from #Mark Hildreth:
"So, you'd have a dictionary of >. Then, your mappings would be mappings[MessageType.ChatMsg] = x => new MsgChat(x);"
There are a couple of ways to interpret this code. One idea is to remove all 100 subclasses and just use one massive class with 100 delegate methods. That is a distant 2nd choice. The other idea and my first choice is for the above code to somehow create a message subclass object. But, I don't quite understand how it would do this. Also, it would be nice to keep the above technique in my Test class of getting all the types or delegates without having to write all 100 of them. Can you or anyone else explain how this can be done?
Instead using a giant switch statement, you can define a Dictionary to map each MessageType value to its defined Message derived class and creates an instance using this mapping data.
Dictionary definition:
Dictionary<int, Type> mappings = new Dictionary<int, Type>();
mappings.Add(MessageType.ChatMsg, typeof(MsgChat));
mappings.Add(MessageType.ResultMsg, typeof(MsgResult));
...
Dictionary consumption:
ConstructorInfo ctor = mappings[MessageType.ChatMsg].GetConstructor(new[] { typeof(Buf) });
Message message = (Message)ctor.Invoke(new object[] { Buf });
Note that I don't compiled this code to verify if is correct or not. I only want to show you the idea.
EDIT
There is my new answer to improve the first one. I'm thinking on your edited question, using given ideas from #MikeSW and #Mark Hildreth.
public class FactoryMethodDelegateAttribute : Attribute
{
public FactoryMethodDelegateAttribute(Type type, string factoryMethodField, Message.MessageType typeId)
{
this.TypeId = typeId;
var field = type.GetField(factoryMethodField);
if (field != null)
{
this.FactoryMethod = (Func<byte[], Message>)field.GetValue(null);
}
}
public Func<byte[], Message> FactoryMethod { get; private set; }
public Message.MessageType TypeId { get; private set; }
}
public class Message
{
public enum MessageType
{
ChatMsg,
}
}
[FactoryMethodDelegate(typeof(ChatMsg), "FactoryMethodDelegate", Message.MessageType.ChatMsg)]
public class ChatMsg : Message
{
public static readonly MessageType MessageTypeId = MessageType.ChatMsg;
public static readonly Func<byte[], Message> FactoryMethodDelegate = buffer => new ChatMsg(buffer);
public ChatMsg(byte[] buffer)
{
this.Buffer = buffer;
}
private byte[] Buffer { get; set; }
}
public class TestClass
{
private IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) where TAttribute : Attribute
{
return from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
where t.IsDefined(typeof(TAttribute), inherit)
select t;
}
[Test]
public void Test()
{
var buffer = new byte[1];
var mappings = new Dictionary<Message.MessageType, Func<byte[], Message>>();
IEnumerable<Type> types = this.GetTypesWith<FactoryMethodDelegateAttribute>(true);
foreach (var type in types)
{
var attribute =
(FactoryMethodDelegateAttribute)
type.GetCustomAttributes(typeof(FactoryMethodDelegateAttribute), true).First();
mappings.Add(attribute.TypeId, attribute.FactoryMethod);
}
var message = mappings[Message.MessageType.ChatMsg](buffer);
}
}
You're on a right track and using a dictionary is a good idea. If reflection is too slow you can use expressions, like this (I'm assuming you decorate the Messages classes with a MessageTypeAttribute).
public class Test
{
public Test()
{
var dict=new Dictionary<MessageType,Func<Buffer,Mesage>>();
var types=from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
where t.IsDefined(MessageTypeAttribute, inherit)
select t;
foreach(var t in types) {
var attr = t.GetCustomAttributes(typeof (MessageTypeAttribute), false).First();
dict[attr.MessageType] = CreateFactory(t);
}
var msg=dict[MessageType.Chat](Buf);
}
Func<Buffer,Message> CreateFactory(Type t)
{
var arg = Expression.Parameter(typeof (Buffer));
var newMsg = Expression.New(t.GetConstructor(new[] {typeof (Buffer)}),arg);
return Expression.Lambda<Func<Buffer, Message>>(newMsg, arg).Compile();
}
}

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);
}
}

Generic inheritance

I have a couple of questions about my understanding of an area of .NET.
Please consider the following mockup:
interface IListInterface<T>
{
//brevity
}
interface IClassInterface
{
int Count { get; }
}
class A<T> : IClassInterface
{
List<IListInterface<T>> MyList = new List<IListInterface<T>>();
public virtual int Count { get { return MyList.Count; } }
public void Add(IListInterface<T> item) { MyList.Add(item); }
public IEnumerable<String> GetAllAsString(T source) { return MyList.Select(o=>o.ToString()); }
}
class B<T1, T2> : A<T1>
{
List<IListInterface<T2>> MyList = new List<IListInterface<T2>>();
public override int Count { get { return base.Count + MyList.Count; } }
public void Add(IListInterface<T2> item) { MyList.Add(item); }
public IEnumerable<String> GetAllAsString(T1 source1, T2 source2)
{
return base.GetAllAsString(source1).Union(MyList.Select(o => o.ToString()));
}
}
class C<T1, T2, T3> : B<T1, T2>
{
List<IListInterface<T3>> MyList = new List<IListInterface<T3>>();
public override int Count { get { return base.Count + MyList.Count; } }
public void Add(IListInterface<T3> item) { MyList.Add(item); }
public IEnumerable<String> GetAllAsString(T1 source1, T2 source2, T3 source3)
{
return base.GetAllAsString(source1, source2).Union(MyList.Select(o => o.ToString()));
}
}
My questions are:
What is the term used to describe what Class B and Class C are doing? Generic type overload inheritance?
There tends to be a lot of repeated code when doing writing such an object, especially when adding more methods which end up just calling its base, and adding its own information to the return. Is there a better way to go about this to allow for a more maintainable class file?
Edited to address the necessity of this method
By using this type of inheritance, one could define a single object which would constrain the requirements for data input and explain its usage.
var x = new C<String, int, DateTime>();
You now know the types which make up the object and you will get a compile-time error if you attempt to call x.GetAllAsString(0, "hello", "world");
This type of object may not work for you, and its fitness for use is not the subject of my question. My questions are about the name of this method and about code reuse in this situation.
[For brevity; for the purposes of my answer, I'm going to focus only on the 'Add' method, as the question/solution applies to your entire model]
Unfortunately, I don't believe you can simplify what you have already implemented. In effect, what you are aiming to do is to constrain a type ('C') at runtime to a set of available types, which (if it worked!) would give you a limited subset of Add/GetAllAsString methods.
So, after going through the compiler, it sounds like you're hoping to turn a single class with a single method like;
public class Base<T>
{
Add(IListInterface<T> o);
}
into a runtime object that exposes an interface like;
public class C
{
Add(IListInterface<string> o) { ... }
Add(IListInterface<DateTime> o) { ... }
Add(IListInterface<int> o) { ... }
}
But, you can't really use generics in that way. The only way to really accomplish this is to approach it the way you have; with a stack of derived types which each add another constrained method to your type.

Conversion of IEnumerable<T> for Extension Method Issue

I have the following class and extension class (for this example):
public class Person<T>
{
public T Value { get; set; }
}
public static class PersonExt
{
public static void Process<TResult>(this Person<IEnumerable<TResult>> p)
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
}
I was expecting I could write something like the following and it would work, but it doesn't:
var x = new Person<List<String>>();
x.Process();
Since List is lower in the inheritance tree than IEnumerable, shouldn't this be valid? It works if I new up a Person<IEnumerable<String>> of course because that's the direct type.
I'm trying to use an extension method that can be applied to all Person<T>'s as long as T implements IEnumerable<Something> because I need to use the .Any() method.
EDIT: Maybe my understanding of covariance is off? I know IEnumerable<String> should convert to IEnumerable<Object>, but couldn't IList<String> convert to IEnumerable<String>?
EDIT2: Forgot to mention that I am using .net 4.0.
I know IEnumerable<String> should
convert to IEnumerable<Object>, but
couldn't IList<String> convert to
IEnumerable<String>?
IList<String> can convert to IEnumerable<String>. The problem is that you're trying to convert Person<List<String>> to Person<IEnumerable<String>>, which is illegal. For example, it's perfectly valid to write:
var x = new Person<IEnumerable<String>>();
x.Value = new string[0];
since Value is of type IEnumerable<String> and a string array is an IEnumerable<String>. However, you cannot write:
var x = new Person<List<String>>();
x.Value = new string[0];
since Value is of type List<String>. Since you can't use a Person<List<String>> in all places where you could use a Person<IEnumerable<String>>, it's not a legal cast.
Note that you can do something similar to what you want if you add a second type parameter to your extension method:
public static void Process<TResult, TList>(this Person<TList> p)
where TList : IEnumerable<TResult>
{
Console.WriteLine(p.Value.Any());
}
Unfortunately, the compiler won't be able to infer both type parameters, so you would have to call it like this:
var x = new Person<List<String>>();
x.Process<String, List<String>>();
If you are using C# 4.0 and can use covariance, then you can define a covariant interface for person:
public interface IPerson<out T>
{
T Value { get; }
}
public class Person<T>
: IPerson<T>
{
public T Value { get; set; }
}
And then write your extension method as:
public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p)
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
Since IPerson<T>.Value is read-only, a IPerson<List<String>> can be used everywhere that an IPerson<IEnumerable<String>> can be, and the conversion is valid.
I'm not sure you've quite grasped the correct use of generics. In any event ...
The only thing that is incorrect is your declaration of extension method, and the way you are attempting to constrain the extension method.
public static class ThingExtensions
{
public static void Process<T>(this Thing<T> p)
where T : IEnumerable<string>
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
}
All I've really done is rename Person to Thing so that we're not getting hung up on what a Person<List<string>> really is.
public class Thing<T>
{
public T Value { get; set; }
}
class ListOfString : List<string>
{ }
class Program
{
static void Main(string[] args)
{
var x = new Thing<ListOfString>();
x.Value = new ListOfString();
x.Process();
x.Value.Add("asd");
x.Process();
var x2 = new Thing<int>();
// Error 1 The type 'int' cannot be used as type parameter 'T'
// in the generic type or method
// 'ThingExtensions.Process<T>(Thing<T>)'.
// There is no boxing conversion from 'int' to
// 'System.Collections.Generic.IEnumerable<string>'.
//x2.Process();
Console.Read();
}
}
You could also move the generic constraint to the Thing<T> if that was more applicable.
You mention covariance, but don't actually use it. You have to specify in or out on your generic parameters. Note that co/contravariance doesn't work on class types; they must be applied to interfaces.
So, introducing an interface and making it covariant:
public interface IPerson<out T>
{
T Value { get; }
}
public class Person<T> : IPerson<T>
{
public T Value { get; set; }
}
public static class PersonExt
{
public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p)
{
// Do something with .Any().
Console.WriteLine(p.Value.Any());
}
}
allows this code to compile:
var x = new Person<List<String>>();
x.Process();

Categories