Unable to use shared 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 is conceptually 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 in an instance of FooService - no matter what type the selected instance of FooService returns. However, this code raises the following exception:

Cannot pass an object of type "WindowsFormsApplication7.FooService`1 [System.DateTime]" to enter "WindowsFormsApplication7.FooService`1 [System.Object]".

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

You could argue why I created a generic set of FooService. However, this is done to ensure type safety in a safe type environment. In this particular case, however, the FooService should be used in the Web API controller to serve various types of Foo. It should return a response with Foo of T without regard for type T.

+6
source share
3 answers

I think you already gave the correct answer:

FooService<double?> Can never be sent to FooService<object> , and FooService<DateTime> never be applied to FooService<object> .

The use of covariance or contravariance does not change this. At https://msdn.microsoft.com/en-us/library/dd997386.aspx, it says: Value types also do not support variance. And since double? and DateTime are value types, this will never work.

+7
source

If possible, you can customize your model as follows:

 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 will let you 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; 
+1
source

I made an example that really works for reference types, but, as pointed out by @Martin, can never work for value types, since they do not support variance. The where T : class constraint ensures that only link types are accepted.

 public interface IFoo<out T> where T : class { T Data { get; } } public struct Foo<T> : IFoo<T> where T : class { public Foo(T data) : this() { Data = data; } public T Data { get; private set; } } public interface IFooService<out T> where T : class { IFoo<T> Get(string id); } public class FooService<T> : IFooService<T> where T : class { public IFoo<T> Get(string id) { return null; } } 

Using:

 ServiceLocator.Register(new FooService<Bar>(), "ServiceId"); // OK because Bar is a reference type var fooService = (IFooService<object>)ServiceLocator.Get("ServiceId"); var foo = fooService.Get("FooId"); 
0
source

Source: https://habr.com/ru/post/985258/


All Articles