6

I want to cast an object as an generic custom Interface.

This is my interface:

public interface IContainer<T>
{
    IEnumerable<T> SaveToCache(IEnumerable<T> models);
}

public class Container<T>
{
    IEnumerable<T> SaveToCache(IEnumerable<T> models);
}

I'm using this interface in a Serviceclass:

public class Manager()
{
    public Manager()
    {
        address = new Container<Address>(this);
        // more containers are constructed here...
    }

    private static Container<Address> address;
    public static Container<Address> Address => address;

    // here are more properties of Type Container<T>
}

Now I want to call the SaveToCacheAsync method dynamically like this:

private void SaveItems(IEnumerable<object> items, string typeName)
{
    object container = typeof(Manager)
        .GetRuntimeProperty(typeName).GetValue(null, null);
    var t = Type.GetType($"MyNameSpace.Models.{typeName}");

    // variant A - doesn't work, but makes clear what I want to do
    (container as IContainer<t>).SaveToCache(items);

    // variant B - doesn't work either
    var saveMethod = container.GetType()
        .GetRuntimeMethod("SaveToCache", new Type[] { Type.GetType($"System.Collections.Generic.List`1[{t.FullName}]") })
        .MakeGenericMethod(new Type[] { t });

    saveMethod.Invoke(container, new object[] { });
}

The project is a PCL, therefore I used GetRuntimeMethods.

Daniel
  • 511
  • 10
  • 25

1 Answers1

1

Your second version does not work because the method itself is not generic, the class is, and the Type instance you have is already an instantiated generic type since you get it form an object instance. While this version works it is not ideal as it involves using reflection to call the method which is slow and generally seems like a code smell

var saveMethod = container.GetType()
    .GetRuntimeMethod("SaveToCache", new Type[] { typeof(IEnumerable<>).MakeGenericType(t) })
    .Invoke (container, new object[] { items });

A better approach would be to have a non generic version of your interface (much like IEnumerable<T> and IEnumerable)

public interface IContainer
{
    IEnumerable SaveToCache(IEnumerable models);
}
public interface IContainer<T> : IContainer
{
    IEnumerable<T> SaveToCache(IEnumerable<T> models);
}

Your class can implement IContainer explicitly to avoid the non generic method being called and use it only in the context of your SaveItems method

public class Container<T> : IContainer<T>
{
    public IEnumerable<T> SaveToCache(IEnumerable<T> models)
    {
        return models;
    }

    IEnumerable IContainer.SaveToCache(IEnumerable models)
    {

    }
}

var container = new Container<string>();
container.SaveToCache(new string[] { "" }); // The generic method is avaiable if we have an referecne to the class
container.SaveToCache(new int[] { 0 });// And this will be a compile time error as expected

IContainer icontainer = container;
icontainer.SaveToCache(new string[] { "" }); // The non genric method will be called 
icontainer.SaveToCache(new int[] { 0 });// And this will be a runtime time error 
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • Your syntax `typeof(IEnumerable<>).MakeGenericType(t)` isn't working for me, I will try the non generic interface... – Daniel Feb 26 '18 at 10:22
  • @DanielR. any specific error you are getting ? I changed it after posting as in missed passing the parameter, but it shoudl work – Titian Cernicova-Dragomir Feb 26 '18 at 10:24
  • The usage of type "IEnumerable" (generic) requires 1-typeargument. – Daniel Feb 26 '18 at 10:27
  • @DanielR. That is strange, you can't declare variables of unintsnatiated generics, but you can use them in `typeof` I did not check on PCL, but this is a language feature and should not depend on this. I am not at PC right now but I will check as soon as I get to one – Titian Cernicova-Dragomir Feb 26 '18 at 10:33
  • @DanielR. I have tried to compile the code in a PCL, a .NET Standard Library and a .NET Core Library, it compiles in all versions, I am not sure why this error occurs for you. – Titian Cernicova-Dragomir Feb 26 '18 at 11:25
  • Ok, thanks for your efforts. I didn't rellay made it working with any version, so i made it non-generic with a huge switch statement (thank god, templates) – Daniel Feb 27 '18 at 07:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/165874/discussion-between-titian-cernicova-dragomir-and-daniel-r). – Titian Cernicova-Dragomir Feb 27 '18 at 08:29