C# Generic Interfaces casting issue - c#

Should this be possible using c# 4 and VS 2010? I am doing some processing on class(es) which implements generic interface(s), and after processing would like to convert the objects to a simpler interface so I can extract certain properties defined by common Interfaces.
interface IMyInterface
{
public Id { get; set; }
}
interface IFile<T1, T2> where T1 : IMyInterface where T2 : IMyInterface
{
Int64 prop1 { get; set; }
T1 t1 { get; set; }
T2 t2 { get; set; }
}
ClassA : IMyInterface
{
... Implement some properties plus interface
public Id { get; set; }
}
ClassB : IMyInterface
{
... Implement some properties plus interface
public Id { get; set; }
}
For example this class has ClassX and ClassY which I want to be certain types for processing/saving, but after that I only want to extract common properties like an ID which is common amongst all classes that Implement this generic Interface (other properties are not common in t1, t1)
ClassSomething : IFile<ClassA, ClassB>
{
... Implement properties plus interface
public ClassX t1
{ get {} set {} }
public ClassY t2
{ get {} set {} }
}
IList<IFile<TItem1, TItem2>> list = new List<IFile<TItem1, TItem2>>()
ClassA ca = new ClassA();
... Fill in the interface defined properties
... Fill the list with objects of ClassSomething
foreach (IFile<TItem1, TItem2> x in list)
{
// This fails
IFile<IMyInterface, IMyInterface> interfaceItem =
(IFile<IMyInterface, IMyInterface>)x;
}
The cast of x above (t1 and t2 properties specifically) to the simpler IMyInterface interface fails.
There are quite a few generic interface questions but I didn't see (or recognize?) any solutions.

The solution you are looking for is called variance (covariance and contravariance). However your IMyInterface cannot be made either covariant or contravariant in T1 and T2, because it has both public getters and public setters accepting T1 and T2:
interface IAnimal {}
class Dog : IAnimal { public void Bark () ; }
class Cat : IAnimal { public void Meow () ; }
var dogs = new FileImpl<Dog, Dog> () ;
dogs.t1 = new Dog () ;
var file = (IFile<IAnimal, IAnimal>) dogs ; // if this were OK...
file.t1 = new Cat () ; // this would have to work
dogs.t1.Bark () ; // oops, t1 is a cat now

Just to expand on Anton Tykhyy's answer,
Yes, you could achieve this with C# 4, provided that you are willing/able to make the following change to your IFile interface:
interface IFile<out T1, out T2> where T1 : IMyInterface where T2 : IMyInterface
{
Int64 prop1 { get; set; }
T1 t1 { get; }
T2 t2 { get; }
}
I've added the out keyword to the generic parameters, and I've removed the set; from the t1 and t2 properties.

Why just not access x.T1 and x.T2 and then cast both to IMyInterface?
foreach (IFile<TItem1, TItem2> x in list)
{
var t1 = x.T1 as IMyInterface;
var t2 = x.T2 as IMyInterface;
}

I think you're confusing the generic constraits with inheritance. The one has nothing to do with the other. The constraints on the generic interface are compiler instructions that tell the compiler that the generic arguments must meet a specific requirment. Logically, to us, that means that ClassSomething definitely has an implementation of IMyInterface. But that's us, the compiler doesn't translate those constraints to any kind of inheritence mapping, so it still only knows that it's an instance of ClassSomething which implements IFile<ClassA, ClassB>. So it won't let you directly cast that to IFile<IMyInterface, IMyInterface>.

Related

c# generic interfaces and inheritance

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.

C# Generic with limited return types

In C# I want to make some specialized generics that will be used just to return a specific type form another generic. The specialized generic's purpose is to force just some exact types that will be returned (like double, double[], byte, byte[]). Probably best is to explain by an example
var x = new MyGeneric<MyInterfaceDouble>();
double returnVal = x.getVal();
var x = new MyGeneric<MyInterfaceMyClass>();
MyClass returnVal = x.getVal();
So I have tried several ways to achieve this but unable to do so. Latest iteration is:
public interface IMyInterface
{}
public interface IMyInterface<T, U> :IMyInterface
{
U getValue();
}
public class MyInterfaceDouble: IMyInterface<MyInterfaceDouble, double>, IMyInterface
{
public double getValue()
{
return 8.355;
}
}
public class MyGeneric<T> where T : IMyInterface
{}
But I can't access the get value
var x = new MyGeneric<MyInterfaceDouble>();
double returnVal = x.getVal(); // not available
How can this be made?
It seems you are going to have some changes in your design.
There is no any definiation for getVal inside IMyInterface, So is natural not available for MyGeneric<MyInterfaceDouble>.
You would inherit from IMyInterface<T, U> instead of IMyInterface:
public class MyGeneric<T> where T : IMyInterface<T, SomeSpecialType>
{}
OR
change IMyInterface defination to have getVal as general which returns object:
public interface IMyInterface
{
object getValue();
}
OR
Change MyGeneric<T> definition to this:
public interface IMyInterface
{ }
public interface IMyInterface<T>
{
T getVal();
}
public class MyInterfaceDouble : IMyInterface<double>, IMyInterface
{
public double getVal()
{
return 8.355;
}
}
public class MyGeneric<T> where T : IMyInterface
{
T Obj { get; }
}
and use like this:
var x = new MyGeneric<MyInterfaceDouble>();
double returnVal = x.Obj.getVal(); // available
Also there are some other solutions which depends on your vision that you want to design.

Implicitly convert generic classes

I'm trying to accomplish something like this.
public class GreatClass<A, B>
where A : IA
where B : IB, new()
{
public void Function()
{
var list = new List<A>();
var obj = new B { Items = list.ToArray() };
}
}
public interface IA
{
}
public interface IB
{
IA[] Items { get; set; }
}
namespace Namespace1
{
public class A : IA {}
public class B : IB {}
}
namespace Namespace2
{
public class A : IA {}
public class B : IB {}
}
So I have two namespaces with similar classes which implements the same interface. I have some logic which I want to share between the classes in the two namespaces and am therefore using generic classes which implements the implemented interface.
However I get a compile error on "list.ToArray()" stating that it Cannot implicitly type 'A[]' to 'IA[]'. Is there something I can do which makes the compiler understand how to handle this?
Using .Cast() like a few of you have been suggesting gives me a runtime error instead. That I can't convert type IA[] to Namespace1.A[] (or Namespace2.A[]).
There are several options, but the best one is to strongly type your B class and make it generic.
This is the code that will do. Note that TA is the type of A you are passing in, thus always allowing to access the members strongly typed.
public interface IB<TA> where TA : IA
{
TA[] Items { get; set; }
}
And change GreaterClass to this:
public class GreatClass<A, B>
where A : IA
where B : IB<A>, new()
{
}
Simply declare your list like this:
var list = new List<IA>();

self-referencing generic inheritance in c#

i saw some c# asp.net source code written as below:
public class EntityInstanceContext<TEntityType> : EntityInstanceContext
{
/// <summary>
/// Initializes a new instance of the <see cref="EntityInstanceContext{TEntityType}"/> class.
/// </summary>
public EntityInstanceContext()
: base()
{
}
can anyone help me to understand why the generic type is subclassing from non-generic one? and what is benifit of designing in that way?
The .NET TypeSystem is a very powerful one.
Imagine the following scenario. I'm writing a class called MyTuple which is a poorly coded clone of the BCL Tuple class:
public class MyTuple<T1, T2> {
public T1 Item1 { get; private set; }
public T2 Item2 { get; private set; }
public MyTuple(T1 item1, T2 item2) {
this.Item1 = item1;
this.Item2 = item2;
}
}
And then I realize I want to make a factory kind of method for that type
so that I can successfully hook into the type inference system and not specify T1 and T2 when I don't have to like so:
new MyTuple<int, string>(123, "test"); // which is also a bit redundant
So I'm writing the method I was talking about in a class, let's call the class Factory:
public class Factory {
public static MyTuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) {
return new MyTuple<T1, T2>(item1, item2);
}
}
That way, I'm happier when writing:
var tuple = Factory.Create(123, "test"); // and tuple is inferred to be of <int, string>
Now what would happen if I renamed Factory to MyTuple:
public class MyTuple {
public static MyTuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) {
return new MyTuple<T1, T2>(item1, item2);
}
}
In short: nothing bad
It is simply the case that I now have 2 completely distinct types:
MyTuple (nongeneric)
MyTuple < T1, T2 >
They have nothing in common, they are different types.
And am I allowed to say that MyTuple<T1, T2> just happens to extend MyTuple ?
Well, as long as MyTuple is neither static nor sealed, yeah, sure !
public class MyTuple { ... }
public class MyTuple<T1, T2> : MyTuple { ... }
So in your case it's nothing more that Mammal extending Animal or ... Tiger extending Mammal.
It's not like Mammal of a weirder sort extending Mammal of a good ol' classical sort.

Implementing twice the same generic interface, with type argument constraints

Having
public interface IGeneric<T>{}
public class Student{}
public class Teacher{}
this is possible
public class ConcreateClass : IGeneric<Student>, IGeneric<Teacher>{}
this is not possible
public class GenericClass<T1, T2> : IGeneric<T1>, IGeneric<T2> {}
because GenericClass<String, String> instance; would cause ambiguity for the two interface implementation. But why this is not possible
public class GenericClassWithTypeConstraints<T1, T2> : IGeneric<T1>, IGeneric<T2>
where T1 : Student
where T2 : Teacher
{}
as T1 and T2 cannot be of the same class? (The compiler error is the same as in the case without type constrains)
Edit
"Soner Gönül" in Why does this result in CS0695 proposed a workarround, using two levels of class inheritance, like this:
public interface IGeneric<T> { String Name { get; } }
public class Student{}
public class Teacher{}
public class GenericClassBase<T1> : IGeneric<T1>
where T1 : Student
{ String IGeneric<T1>.Name { get { return "Generic class of Student"; } } }
public class GenericClassDerived<T1, T2> : GenericClassBase<T1>, IGeneric<T2>
where T1 : Student
where T2 : Teacher
{ String IGeneric<T2>.Name { get { return "Generic class of Teacher"; } } }
Code like this then produce expected result
GenericClassDerived<Student, Teacher> X = new GenericClassDerived<Student, Teacher>();
Console.WriteLine(((IGeneric<Student>)X).Name); //outputs "Generic class of Student"
Console.WriteLine(((IGeneric<Teacher>)X).Name); //outputs "Generic class of Teacher"
as T1 and T2 cannot be of the same class?
Yes, they can be the same class. Constraints cannot be sealed classes1 so Teacher can be derived from Student or vise versa. It's not logical but compiler has no idea about it.
For example,
using System;
public class Student{}
public class Teacher : Student{}
public class GenericClassWithTypeConstraints<T1, T2>
where T1 : Student
where T2 : Teacher
{}
class Test {
static void Main() {
var obj = new GenericClassWithTypeConstraints<Teacher, Teacher>();
}
}
will compile without any problem. Demo
Docs:
Constraints can also be class types, such as abstract base classes.
However, constraints cannot be value types or sealed classes.

Categories