Tobi M. Ottorony
Tobi M. Ottorony

Reputation: 129

C# Return generic type without type parameter at runtime

Is it possible to return an object of type

 IModel< T >

without knowing the type parameter?

the objects are stored in a dictionary, with Type as the key and an object with IModel (base interface to IModel<T>) implemented as a value. The interface IModel<T> inherits from IModel, but to do the full action I need an object of IModel<T>. T needs to have the interface IFactoryItem.

but first the code:

    public IModel<T> GetModel<T>() where T : IFactoryItem
    {
         Type tmpType  = typeof(T);
         if (!_allModelsByType.ContainsKey(tmpType))
                return null;

         return (IModel<T>)_allModelsByType[tmpType];
    }

i thought of a solution like

public IModel GetModel(Type t) and a wrapper to cast it to the right type.

I hope that i am not totally wrong. this is my first question.

Upvotes: 3

Views: 4134

Answers (2)

Meirion Hughes
Meirion Hughes

Reputation: 26398

If your question is how to return an instance of IModel<T> but you don't know what T is at compile time, only that it is always derived from IFactoryItem then:

If you don't use T in a method input, and T is a class you can use covariance:

public interface IModel<out T> where T : class
{
    T Value { get; }
}

public class Model<T> : IModel<T> where T : class
{
    public T Value { get; set; }
}


class Program
{
    static void Main(string[] args)
    {
        var foo = new Model<string>()
        {
            Value = "hello world",
        };

        IModel<object> boo = foo;

        Console.WriteLine(boo.Value);
    }
}

That way you can pass around IModel<IFactoryItem> rather than IModel<T>

If you need value types though, or you can't use covariance then ideally you'd have (as you suggest) a second non-generic interface IModel that exposes any values as object

public interface IModel
{
    object Value { get; }
}

public class Model<T> : IModel, IModel<T> 
{
    public T Value { get; set; }
    object IModel.Value => Value;
}

If your question is how to make an instance of Model<T> when you only know the type at run-time then its:

 var someType = typeof (SomeFactoryItem);
 var instance = Activator.CreateInstance(typeof (Model<>).MakeGenericType(someType));

you will still need to either return IModel or, if you can use covarience, IModel<IFactoryItem>

Upvotes: 2

David Pine
David Pine

Reputation: 24525

Is it possible to return an object of type IModel<T> without knowing the type parameter?

Technically no, but I think this was actually asked with a slight misunderstanding. The type of T is know, it is just generic -- in fact it is so well known that it is even constrained. Note the where T : IFactoryItem, this indicates that anything that attempts to execute either the AddModel or GetModel functions must be something that implements the IFactoryItem interface. If you trying writing something that calls either methods and passes into it anything other than an implementation of the IFactoryItem interface, it won't even compile.

Please consider the code below, the variable namely fetch is actually the same object as the variable model that was added!

class Program
{
    static void Main()
    {
        var test = new Test();
        var model = new Model();
        test.AddModel(model);
        var fetch = test.GetModel<FactoryItem>();

        Console.WriteLine(fetch == model 
                              ? "It does in fact work..." 
                              : "Uh, that was supposed to work?");
        Console.ReadLine();
    }

    public class Test
    {
        private readonly Dictionary<Type, object> _allModelsByType;

        public Test()
        {
            _allModelsByType = new Dictionary<Type, object>();
        }

        public void AddModel<T>(IModel<T> model) where T : IFactoryItem
        {
            _allModelsByType.Add(typeof(T), model);
        }

        public IModel<T> GetModel<T>() where T : IFactoryItem
        {
            var tmpType = typeof(T);
            return _allModelsByType.ContainsKey(tmpType)
                ? _allModelsByType[tmpType] as IModel<T>
                : null;
        }
    }
}

internal class FactoryItem : IFactoryItem { }

internal class Model : IModel<FactoryItem>
{
    public FactoryItem Value { get; set; }
}

internal interface IFactoryItem { }

internal interface IModel<T> where T : IFactoryItem
{
    T Value { get; set; }
}

Running this program will output "It does in fact work..."

<iframe width="100%" height="750" src="https://dotnetfiddle.net/Widget/J26ETn" frameborder="0"></iframe>

Upvotes: 0

Related Questions