Generics With Type Uknown At Compile Time
The following is an example of a service containing both generic method calls and generic types.
public interface IContainer<T>
{
IEnumerable<IProperty<T>> Properties { get; }
}
public interface IPropertyService
{
IContainer<T> GetProperties<T>(string propertyCode);
}
Now suppose that you don't know the actual type of the data until runtime (possibly because the type is retrieved from a DB or from some other service). You need a way to interact with these generics, and Reflection can help you here.
Let's assume that you have managed to get the Type
of the data that you
are handling (such as string
, decimal
, double
, etc...)
and that you have a concrete implementation of the IPropertyService
(perhaps through Dependency Injection).
Once you have that, you can invoke the GetProperty
method in the following way:
Type type = // ... this is the actual runtime type
IPropertyService service = // ... this is an instance of the service
Type serviceType = typeof(IPropertyService);
MethodInfo getPropertiesInfo =
serviceType.GetMethod("GetProperties", new Type[] { typeof(string) });
if (getPropertiesInfo == null)
{
// Uh-oh.... has the interface changed after we wrote this? Throw an exception...
}
Type[] genericArguments = { type }; // we only have one generic argument (<t>)
MethodInfo getPropertiesMethodInfo =
getPropertiesInfo.MakeGenericMethod(genericArguments);
// Finally invoke the method to search for a price
var propertiesContainer =
getPropertiesMethodInfo.Invoke(service, new object[] { "price"});
As you can see are quite a few steps to be performed, but at the end, through reflection, you end up with a container filled with all the objects that you have required.
Now assume that you want to access to the single properties contained in the IContainer.
You need some way to invoke the C# property Properties
. defined on IContainer
.
Similarly to the MethodInfo
class, System.Reflection has a
PropertyInfo class
that you can use to work with Properties.
The following solution, despite compiling fine, will not work at run-time:
PropertyInfo containerInfo = typeof(IContainer<>).GetProperty("Properties"); // WRONG!!!
if (containerInfo == null)
{
// the interface has changed. Throw an exception
}
var properties = containerInfo.GetValue(container, null) as IEnumerable;
If you try to run this, you will be greeted by the following exception: Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
After scratching my head for a while, I realized that I should have
first created a generic type at run-time before retrieving the PropertyInfo
:
Type containerType = typeof(IContainer<>).MakeGenericType(type);
PropertyInfo containerInfo = containerType.GetProperty("Properties");
if (containerInfo == null)
{
// the interface has changed. Throw an exception
}
var properties = containerInfo.GetValue(container, null) as IEnumerable;
At this point I have a collection of IProperties
which I can enumerate through.
If needed, since the types in this collection are also generic, I need to repeat the process above
until I reach a fundamental type (such as string
, double
, etc...).
Leave a Comment