Reputation: 577
I have a WebAPI method and in it I have an IEnumerable<T>
of objects where the type of T is not known until runtime. So, I need to execute a foreach
on the IEnumerable<T>
collection. The problem is I cannot replace the T with a specific type since that type is not known until runtime. So, I get the type of T using some reflection in a helper method and store it into a variable "type". But apparently, I cannot use IEnumerable<type>
. It throws an error saying a variable cannot be used as a type.
My WebAPI method needs to log activity whenever an update happens to certain types of data. So, I have a custom ActionFilter attribute setup to do the dirty work.
Here is my WebAPI method (this is just a dummy test method):
[HttpPost]
[Route("testlogging")]
[PhiLog]
public IEnumerable<Person> UpdateTestData(Person person)
{
IList<Person> persons = new List<Person>();
persons.Add( person );
persons.Add( person );
return persons;
}
When OnActionExecuted is executed, we don't know the type of response content returned to the caller. It could be just null, or just a string, or just an int or IList<T>
or IEnumerable<T>
and the type of T is not known. Here is my Custom Attribute:
public class PhiLogAttribute : ActionFilterAttribute
{
public override void OnActionExecuted( HttpActionExecutedContext actionExecutedContext )
{
var user = AuthenticationHelper.GetCurrentUserFromRequest( actionExecutedContext.Request );
var actionDescriptor = actionExecutedContext.Request.GetActionDescriptor();
var controllerName = actionDescriptor.ControllerDescriptor.ControllerName;
var methodName = actionDescriptor.ActionName;
var action = string.Format( "{0}/{1}", controllerName, methodName );
var responsePayload = actionExecutedContext.Response.Content as ObjectContent;
var payloadType = responsePayload.ObjectType;
/* The variable below (payloadObj) could be of any type - an int, a
string or a generic ICollection. The code below is just assuming
it is of type IList<Person>. This is for my test purpose */
var payloadObj = responsePayload.Value;
/* AppHelper.GetEnumerableType returns the type of the generic
parameter T. In this case, a Person type */
var type = AppHelper.GetEnumerableType( payloadObj.GetType());
if ( payloadType.GetInterfaces().Any( x => x.GetType().Name == "ICollection`1") )
{
/* This is where I am stumped. I need to walk this
ICollection<T> and log some value from few properties of T.
At runtime, payloadObj is of type object. I need to cast it
into the correct ICollection type */
foreach (var x in (ICollection<type>)payloadObj)
{
//do something with var x here.
/* But ICollection<type> throws an error "type is a variable
but used like a type" */
}
}
}
}
Here is my helper method to get the type of the type parameter T.
public static Type GetEnumerableType( Type type )
{
var interfaceTypes = type.GetInterfaces();
foreach ( Type interfaceType in interfaceTypes )
{
if ( interfaceType.IsGenericType && interfaceType.Name == "ICollection`1" )
return interfaceType.GetGenericArguments()[ 0 ];
}
return null;
}
I have commented inline in the code where the problem is. Anyone please tell me how can I use a variable in place of T in an IEnumerable<T>
. Thanks.
Upvotes: 3
Views: 1261
Reputation: 55529
How can I use a variable in place of T in an
IEnumerable<T>
?
You can't. Generic type arguments must be known at compile time.
Fortunately, in your particular scenario, there's a simple alternative. Because the ICollection<T>
interface inherits the non-generic IEnumerable
interface, you can just cast your object to IEnumerable
and enumerate that:
// using System.Collections;
foreach (object x in (IEnumerable)payloadObj)
{
// Access properties of x using reflection or by casting to a known type...
}
Upvotes: 2