Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to cast generic object

I have the following conceptual model:

public interface IFoo<out T>
{
    T Data { get; }
}

public struct Foo<T> : IFoo<T>
{
    public Foo(T data) : this()
    {
        Data = data;
    }

    public T Data { get; private set; }
}

public class FooService<T> 
{
    ...

    public Foo<T> Get(string id)
    {
        ...
    }
}

Then I try to use it in a way that conceptually is equivalent to this:

// Create and register a few FooService instances
ServiceLocator.Register(new FooService<DateTime>(), "someServiceId");
ServiceLocator.Register(new FooService<double?>(), "anotherServiceId");

// Retrieve a particular FooService instance and call the Get method
var fooService = (FooService<object>)ServiceLocator.Get("someServiceId");
var foo = fooService.Get("someFooId");

I want to use the Get() method on a FooService instance - no matter what type the selected FooService instance returns. However, this code results in the following exception:

Unable to cast object of type 'WindowsFormsApplication7.FooService`1[System.DateTime]' to type 'WindowsFormsApplication7.FooService`1[System.Object]'.

Any suggestions on how to solve this problem would be greately appreciated.

You could argue, why I made the FooService generic in the first place. However, this is done to secure type safety in a type safe environment. In this particular case, though, the FooService shall be used in a Web API controller to serve various types of Foo. It shall return a response with Foo of T disregarding the type of T.

like image 630
Lars Michael Avatar asked Apr 16 '15 11:04

Lars Michael


2 Answers

I think you have already given the correct answer:

FooService<double?> can never be casted to FooService<object>, and FooService<DateTime> can never be casted to FooService<object>.

Using covariance or contravariance does not change that. The page https://msdn.microsoft.com/en-us/library/dd997386.aspx states: Value types also do not support variance. And since double? and DateTime are value types, this can never work.

like image 185
Martin Mulder Avatar answered Sep 26 '22 01:09

Martin Mulder


If possible, you could adjust your model like this:

public interface IFoo
{
    object Data { get; }
}

public interface IFoo<T> : IFoo
{
    new T Data { get; }
}

public class Foo<T> : IFoo<T>
{
    public Foo(T data) { Data = data; }
    public T Data { get; private set; }
    object IFoo.Data { get { return Data; } }
}

public interface IFooService
{
    IFoo Get(string id);
}

public interface IFooService<T> : IFooService
{
    new IFoo<T> Get(string id);
}

public class FooService<T> : IFooService<T>
{
    public IFoo<T> Get(string id) { return null; }
    IFoo IFooService.Get(string id) { return Get(id); }
}

which would enable you to do

ServiceLocator.Register(new FooService<DateTime>(), "someServiceId");
ServiceLocator.Register(new FooService<double?>(), "anotherServiceId");

// Retrieve a particular FooService instance and call the Get method
IFooService fooService = (IFooService)ServiceLocator.Get("someServiceId");
IFoo foo = fooService.Get("someFooId");
object data = foo.Data;
like image 35
Michael Sander Avatar answered Sep 24 '22 01:09

Michael Sander