views:

231

answers:

6

Hi,

I need to use a generic interface like the following:

public interface IContainer<T>
{
    IEnumerable<IContent<T>> Contents { get; }
}

An object implementing this interface is returned by a generic method like the following:

IContainer<T> GetContainer<T>(IProperty property);

Type T is unknown until run-time.

Using reflection I am able to invoke the GetContainer<T> method and get the result.

My problem is that I don't know how to enumerate the result which has type Object (therefore I cannot cast it to IEnumerable).

I have also tried casting as follows but it does not work (it says "Type is expected"):

var myContainer = genericMethodInfo.Invoke(
                           myService, 
                           new object[] { property })
    as typeof(IContainer<>).MakeGenericType(type);

where type is the runtime type, myService is the service exposing the GetContainer<T> method, and property is of type IProperty as needed.

UPDATE: see my complete solution in my blog: http://stefanoricciardi.com/2010/02/18/generics-with-type-uknown-at-compile-time/

+1  A: 

Your type T must be known by the compiler, so this will not work. You can try making a non generic version of your interface, like this:

public interface IContainer
{
    IEnumerable<IContent> Contents { get; }
}

public interface IContainer<T> : IContainer { ... }

This way you have something to cast to and are able to use it.

Steven
A: 

If you're thinking of moving to .Net 4 that is what the dynamic type provides.

Dugan
+1  A: 

typeof(IContainer<>).MakeGenericType(type) will only evaluate at runtime, while "as" needs to know the type at compiler time.

What I really don't understand is this comment: My problem is that I don't know how to enumerate the result which has type Object (therefore I cannot cast it to IEnumerable).

myContainer may be an Object but it can surely be cast to IEnumerable? If it can't then it can't be enumerated.

pdr
Thank you, I had overlooked the fact that IContainer (despite its name) actually does *not* implement IEnumerable. That's why the cast was failing. Your comment got me on the right track to solve my issue.
Stefano Ricciardi
A: 

I'm assuming that your object will only be returned as being one of a restricted number of types, so why not test against those before casting e.g. if object is thisclass?

ChrisBD
Unfortunately I cannot make such an assumption because my code is part of a library that might be reused.While I know that mostly strings, doubles and decimals will be handled, I cannot exclude other types "a priori". If all else fails, though, I might have to go that route and raise exceptions for unexpected types.
Stefano Ricciardi
@Stefano - test your object type against IEnumerable that way you can ensure that you only cast it e.g. for use in a foreach statement, if your object class inherits IEnumerable.
ChrisBD
A: 

Sorry if I misunderstood, I was having trouble understanding exactly what your objective was. Were you looking for something like this?

var myContainer = typeof(ClassWithGetContainer)
                 .GetMethod("GetContainer")
                 .MakeGenericMethod(runtimeType)
                 .Invoke(InstanceOfClassWithGetContainer, new object[] { property });
thedugas
No, I am already past this point. I have already populated myContainer and it's working. My problem comes afterward, that is retrieving the single contents from the container.
Stefano Ricciardi
+1  A: 

First, when casting you need a type (double, int); typeof takes a type argument and returns a class of type Type.

     object x = 0.0;
     Type t = typeof(double);
     double y = x as t; //does not compile - t is not a type - it's an instance of type Type
     double y = x as typeof(double); //same as above
     double y = x as double; //compiles - double is a type
     Type z = x as Type; //compiles - Type is a type

Second, here is some example code:

using System;
using System.Collections.Generic;
using System.Collections;
using System.Reflection;
using System.Diagnostics;

namespace TryThis
{
   public interface IContainer<T>
   {
      IEnumerable<IContent<T>> Contents { get; }
   }
   public interface IContent<T>
   { 
      T GetMyContent(); 
   }
   public interface IProperty 
   { }

   public class Content<T> : IContent<T>
   {
      T m_content = default(T);
      public T GetMyContent() { return m_content; }
      public Content(T val) { m_content = val; }
   }

   public class Contents<T> : IEnumerable<IContent<T>>
   {
      List<IContent<T>> m_contents = new List<IContent<T>>();
      IEnumerator<IContent<T>> IEnumerable<IContent<T>>.GetEnumerator() { return m_contents.GetEnumerator(); }
      IEnumerator IEnumerable.GetEnumerator() { return m_contents.GetEnumerator(); }
      public Contents(params T[] contents) { foreach (T item in contents) m_contents.Add(new Content<T>(item)); }
   }

   public class TestGenericContent : IContainer<int>
   {
      public IContainer<int> GetContainer(IProperty property) { return this; }
      public IEnumerable<IContent<int>> Contents { get { return new Contents<int>(1, 2, 3); } }
   }

   public static class TryThisOut
   {
      static void Test2(object o)
      {
         Type t = o.GetType();
         Type tInterface = t.GetInterface("IContainer`1"); //could be null if o does not implement IContainer<T>
         Type tGenericArg = tInterface.GetGenericArguments()[0]; //extracts T from IContainer<T>
         MethodInfo info = t.GetMethod("GetContainer");
         IProperty propArg = null; //null in this example
         object oContainer = info.Invoke(o, new object[] { propArg });

         PropertyInfo prop = tInterface.GetProperty("Contents");
         object oContents = prop.GetGetMethod().Invoke(oContainer, null);
         //oContents is of type IEnumerable<IContent<T>>, which derives from IEnumerable, so we can cast
         IEnumerable enumeratedContents = oContents as IEnumerable;

         MethodInfo getContentItem = typeof(IContent<>).MakeGenericType(tGenericArg).GetMethod("GetMyContent");
         foreach (object item in enumeratedContents)
         {            
            object oContentItem = getContentItem.Invoke(item, null);
            Debug.Print("Item {0} of type {1}", oContentItem, oContentItem.GetType());
            //...
         }
      }

      public static void Test()
      {
         object o = new TestGenericContent();
         Test2(o);
      }
   }
}
ILoveFortran