Lets say I have this arrangement:
public interface ICreatable
{
int CreatedByUserId { get; set; }
}
public class Unicorn : ICreatable
{
public int CreatedByUserId { get; set; }
}
public interface ICrudService<T>
where T : class, ICreatable
{
T DoSomething(T t);
}
public class UnicornService : ICrudService<Unicorn>
{
public Unicorn DoSomething(Unicorn unicorn)
{
var createdByUserId = unicorn.CreatedByUserId;
// ...
return unicorn;
}
}
And use it like so:
static void Main(string[] args)
{
var unicorn = new Unicorn();
var unicornService = new UnicornService();
unicornService.DoSomething(unicorn);
}
This runs fine. However, lets say I want to cast unicornService as it's interface type of ICrudService along with it's generic type to it's interface type as such:
var crudService = unicornService as ICrudService<ICreatable>;
I run into problems. This is how it looks:
unicornService as ICrudService<Unicorn> --> casts is fine
unicornService as ICrudService<ICreatable> --> casts to null
It seems since Unicorn derives from ICreatable and since ICrudService<T> where T: class, ICreatable that it should have no problems working this out. My searches started leading me into Covariance and Contravariances but I'm getting lost at that level.
How can I cast crudService to ICrudService<ICreatable>?
Update:
Using covariance as such:
public interface ICrudService<out T>
Then makes intellisense say "Invalid variance: The type parameter 'T' must be contravariantly valid on 'ICrudService.DoSomething(T)'. 'T' is covariant." How does this work?
An ICrudService<Unicorn> cannot be treated as an ICrudService<ICreatable>.
An ICrudService<Unicorn> object is only allowed to accept parameters of type Unicorn or subtypes of Unicorn, but an ICrudService<ICreatable> can accept a parameter of SomeOtherTypeOfICreatable.
You UnicornService type is allowed to use members specific to Unicorn, not just ICreatable, since that's the type it restricted it's function to. That restriction prohibits it from meeting the more general interface's API.
So, in short, it's not possible.
You should change DoSomething to accept ICreatable instead of T to use out T modifier:
public interface ICrudService<out T>
where T : class, ICreatable
{
T DoSomething(ICreatable t);
}
public class UnicornService : ICrudService<Unicorn>
{
public Unicorn DoSomething(ICreatable t)
{
var unicorn = (Unicorn)t;
var createdByUserId = unicorn.CreatedByUserId; // or t.CreatedByUserId
// ...
return unicorn;
}
}
Please note that this code will throw the exception if non-Unicorn will be passed to UnicornService.
Related
I have a simple class, which looks something like this:
public class MyClass<T>
{
public int Status { get; set; }
public T Value { get; set; }
}
For convenience, I have another class which inherits from MyClass<string>, to allow me to construct MyClass without generic arguments, such as:
public class MyClass : MyClass<string> { }
I want to be able to cast MyClass<T> to MyClass, and it seems this doesnt work by default. This example cast throws the following error:
MyClass<string> withT = new MyClass<string> { Status = 1, Value = "Somevalue" };
MyClass withoutT = (MyClass)withT;
Unable to cast object of type 'MyClass`1[System.String]' to 'MyClass'
So I believe I need to implement some implicit/explicit casting logic, as described in this answer.
I've updated MyClass<T> as follows, however the same error is still thrown:
public class MyClass<T>
{
public int Status { get; set; }
public T Value;
public static implicit operator MyClass(MyClass<T> myClass)
{
return new MyClass
{
Status = myClass.Status,
Value = myClass.Value.GetType() == typeof(string) ? myClass.Value.ToString() : null
};
}
}
Can anyone help point out where I'm going wrong?
Why not use a factory? Here's a very simple example:
public static class MyClass
{
public static MyClass<string> Create(string s, int status)
{
return new MyClass<string>(s, status);
}
}
...
var x = MyClass.Create("Foo", 0);
Consider making MyClass<T> a must inherit class (with the abstract keyword) so it cannot be instantiated. This way you can only create objects from derived classes, which you can down cast to the base class.
Also if you want a type reference to the derived types (for casting) then include that in the type parameters. Add a TDerived which much inherit from MyClass<T> as such:
public abstract class MyClass<TDerived, TValue> where TDerived : MyClass<TDerived, TValue>
{
protected MyClass(int status, TValue value)
{
this.Status = status;
this.Value = value;
}
public int Status { get; set; }
public TValue Value { get; set; }
public static implicit operator TDerived(MyClass<TDerived, TValue> myClass)
=> myClass as TDerived;
}
public class SpecificClass : MyClass<SpecificClass, string>
{
public SpecificClass(int status, string value) : base(status, value)
{
// The ONLY way to instantiate a MyClass<> is from a derived class
// such as this.
}
}
class Program
{
static void Main(string[] args)
{
MyClass<SpecificClass, string> my_class = new SpecificClass(-1, "ABC");
SpecificClass my_specific = my_class;
}
}
Jon Skeet is right in his comments.
Throw out generics altogether. Can you do this?
class X {}
class Y : X {}
...
var x = new X();
var y = (Y)x;
No. You cannot. An X is not a Y. Similarly a MyClass<string> is not a MyClass.
Taking it further your casting code still doesn't work as you've stated, and trying to define an implicit or explicit cast operator in other ways (e.g. typed specifically with string) won't even compile.
Solutions
Do l33t's solution. It's the most straight forward.
Do #ja72's solution. It works and is the most generic.
Create your own static methods that do conversion or create extension methods that do the conversion.
Break the inheritance of MyClass from MyClass<string> and define your operators. Yes, that means some redundant code.
the chosen title for my question isn't quite precise, but I don't know the term I'm looking for or if it is even possible.
What I have in mind is a chain of consumer <- procucer <-product.
A consumer can "consume" producers and producers "produce" products of a certain type. Therefor I wrote:
public interface IProduct
{
string ProductName { get; }
}
public class Product : IProduct
{
public string ProductName { get { return "name of product"; } }
}
public interface IProducer<T>
{
T ProducerProperty { get; set; }
void ProducerMethod();
}
public class Producer<T> : IProducer<T> where T : IProduct
{
public Producer()
{
}
public T ProducerProperty { get; set; }
public void ProducerMethod()
{
}
}
public interface IConsumer<T>
{
T ConsumerProperty { get; set; }
void ConsumerMethod();
}
public class Consumer<T> : IConsumer<T>
{
private U producer; //U should be IProducer<IProduct>, doesen't work
public Consumer(U producer) //U should be IProducer<IProduct>, doesen't work
{
this.producer = producer;
}
public T ConsumerProperty { get; set; }
public void ConsumerMethod()
{
}
}
and the use case:
private IProducer<IProduct> producer; //DeviceManager
private IConsumer<IProducer<IProduct>> consumer; //DeviceViewManager
public MainPage()
{
this.InitializeComponent();
producer = new Producer<IProduct>();
consumer = new Consumer<IProducer<IProduct>>();
}
The consumer class uses the generic "U", which is imaginary at this point. I want the consumer class to use the type U. In the context of the given example you could think of a user who consumes different types of noodles from different manufacrures of noodles.
I want the generic classes be tied to the interfaces rather than to actual classes. But I culdn't manage to achieve this. I tried substituting the interfaces with base classes (e.g.: ProducerBase), but than the actual base classes were needed.
The Problem
If you define the Consumer class like this
public class Consumer<T, U> : IConsumer<T, U>
{
...
}
you get the errors
CS0314 The type 'T' cannot be used as type parameter 'T' in the generic type or method IConsumer<T, U>'. There is not boxing conversion or type parameter conversion from 'T' to ''IProducer'.
and
CS0314 The type 'U' cannot be used as type parameter 'U' in the generic type or method IConsumer<T, U>'. There is not boxing conversion or type parameter conversion from 'U' to ''IProducer'.
What this tells you is that you cannot derive from the IConsumer<T, U> interface, because it has constraints for its generic type parameters:
public interface IConsumer<T, U>
where T : IProducer<U> // T needs to "be" an IProducer<U>
where U : IProduct // U needs to "be" an IProduct
{
...
}
because the interface expects T and U to derive from IProducer<U> and IProduct respectively and the compiler doesn't know that the generic type parameters T and U of the Consumer class can actually be converted to an IProducer<U> and IProduct respectively.
The Solution
Therefore, if you define the Consumer like this and add constraints for T and U
public class Consumer<T, U> : IConsumer<T, U>
where T : IProducer<U>
where U : IProduct
{
...
}
it will work because now you specified that T and U will always be an IProducer<U> and IProduct respectively.
I hope above explanation was understandable; either way, you should also read this for a good understanding of constraints on generic types.
If a class satisfies a covariant interface with a derived class as type parameter, why is that not enough to satisfy a constraint for the same interface with the base class as type parameter?
Given the following code
public interface ICovariantInterface<out T>
{
T Value { get; }
}
public class Base { public int Value { get; set; } }
public class Sub : Base { public int OtherValue { get; set; } }
public class A : ICovariantInterface<Sub>
{
Sub _sub = new Sub { Value = 1, OtherValue = 2 };
public Sub Value { get { return _sub; } }
}
public class B : A
{
ICovariantInterface<Base> MeAsInterface { get { return this; } }
}
public interface OtherInterface : ICovariantInterface<Base>
{ int ThirdValue { get; } }
public class C : A, OtherInterface
{
ICovariantInterface<Base> MeAsInterface { get { return this; } }
public int ThirdValue { get { return 2; } }
}
Class B works fine - since A satisfies the covariant interface ICovariantInterface with type parameter Sub, it can immediately be converted to the same interface with type parameter Base - but C fails to compile, with error
'CovarianceTest.C' does not implement interface member 'CovarianceTest.ICovariantInterface.Value'. 'CovarianceTest.A.Value' cannot implement 'CovarianceTest.ICovariantInterface.Value' because it does not have the matching return type of 'CovarianceTest.Base'.
How can C fail to satisfy the constraint when it can immediately be converted to the interface specified in the constraint?
This problem has made it quite difficult for me to take full advantage of the possibilites offered by covariance.
Covariance (and contravariance) establish rules for consuming types with type parameters marked with out (or in), in that, if you have an instance of a particular type certain implicit conversions are available to you, as the consumer.
In your example, B is consuming an instance of A (that happens to be itself, but need not have been) at the point at which your allowed cast occurs.
But in your C example, you're attempting to implement an interface. Covariance and Contravariance don't apply here - if the interface states that a method returns a T then the implementation has to provide a method that returns exactly the type substituted for T.
I have a base service class with virtual method that sets the properties of an object and returns that object.
Then i have one more service which derived from the base service and also overrides the base method. In overriden method, the derived service executes base.DowWork() to set common properties, and then also sets additional properties.
So based on articles here and here I was able to do this using generics.
public interface IResult
{
}
public class BaseResult : IResult
{
public string CommonProperties { get; set; }
}
public class AdditionalResult : BaseResult
{
public string AdditionalProperties { get; set; }
}
public interface IService<T> where T : IResult
{
T DoWork();
}
public class BaseService<T> : IService<T> where T : BaseResult, new()
{
public virtual T DoWork()
{
var t = new T();
t.CommonProperties = "Some Value";
return t;
}
}
public class AdditionalService : BaseService<AdditionalResult>
{
public override AdditionalResult DoWork()
{
var addtionalResult = base.DoWork();
addtionalResult.CommonProperties = "Override value that was set by BaseService";
addtionalResult.AdditionalProperties = "Set additional properties";
return addtionalResult;
}
}
So far so good
Now i want to create a Factory method that will return the instance of a service based on some type. The application will use the factory to get service instance and call DoWork() like below
class Program
{
static void Main()
{
var factory = new MyFactory();
var service = factory.GetService(0);
var iresult = service.DoWork();
// do something here with IResult
}
}
below is the factory method
public class MyFactory
{
public IService<IResult> GetService(int someType)
{
if (someType == 0)
{
return (IService<IResult>)new BaseService<BaseResult>();
}
if (someType == 1)
{
return (IService<IResult>)new AdditionalService();
}
// note I may have more types and services here. But for simplicity i am using only 2
throw new NotSupportedException();
}
}
However i am not able to figure out what should be the signature of this factory method? Based on suggestions here I'm casting service instance but while executing the application I am getting runtime exception
Unable to cast object of type
'BaseService 1[BaseResult]' to
type 'IService 1[IResult]'
if i don't cast the service instance in the Factory then i get compile time error
Cannot implicitly convert type 'BaseService' to
'IService'. An explicit conversion exists (are you missing a
cast?)
See SO question Understanding Covariant and Contravariant interfaces in C#.
You want to use covariance (out keyword). If you add it to your IService interface generic type it works as expected.
public interface IService<out T> where T : IResult
I know SO prefers not to post links but I can't possibly write anything more or better than already answered in that question.
I'm with problems to convert from the type derived to base type using Generics.
Class to manage the dictionary:
public class ManagerDictionary<TContext>
{
public ManagerDictionary()
{
this.Dictionary = new Dictionary<int, TContext>();
}
public IDictionary<int, TContext> Dictionary { get; private set; }
public void Register<TSubContext>(int context, TSubContext subContext) where TSubContext : TContext
{
this.Dictionary[context] = subContext;
}
}
Interface of the Process context:
public interface IProcessContext : IContext<ProcessViewModel>
{
}
My test class:
public class Foo<TViewModelContext> where TViewModelContext : ViewModeBase
{
public Foo(IProcessContext processContext)
{
// Create de Dictionary Manager.
this.ManagerDictionary = new ManagerDictionary<IContext<TViewModelContext>>();
// Register the process context on dictionary.
// The error is occurring here: The is no implicit reference conversion from 'IProcessContext' to 'IContext<TViewModelContext>'
this.ManagerDictionary.Register((int)ContextType.Process, processContext);
}
protected ManagerDictionary<IContext<TViewModelContext>> ManagerDictionary { get; set; }
}
When I try register the processContext, the problem occurs:
The is no implicit reference conversion from 'IProcessContext' to
IContext<TViewModelContext>
How can I resolve this problem?
Edit:
When I Create a inherited class of the Foo, I can register, but I need register on Foo class too.
public interface IAnotherProcessContext : IContext<ProcessTwoViewModel>
{
}
public class InheritedFoo : Foo<ProcessTwoViewModel>
{
public InheritedFoo(IAnotherProcessContext anotherProcessContext)
{
base.ManagerDictionary.Register((int)ContextType.InheritedProcess, anotherProcessContext);
}
}
You're trying to treat IContext<T> as if it's covariant with respect to T, but that interface isn't defined as being covariant.
Either make the interface be covariant, or alter your program such that you never expect an IContext<Child> to be implicitly convertible to an IContext<Parent>.