I want two generic classes to be able to reference each other. I can't seem to get anything to compile. Tried this:
class Program
{
static void Main(string[] args)
{
}
public class ClassA<BT> where BT: ClassB<ClassA<BT>>
{
BT btvar;
}
public class ClassB<AT> where AT: ClassA<ClassB<AT>>
{
AT atvar;
}
}
This has a practical implementation, but I wanted to avoid a complicated explanation of my own code. I can create closed classes that obey the rule, I just can't seem to describe a generic class or interface for those closed instances.
As fas as I understand, this is impossible, and this is why:
You want A, with a template value of type B.
You want B, with a template value of type A.
If you create a new instance of A, the compiler has to check of T is of type B. To check if it's type B, it has to check if B is of type A, A of type B, etc etc.
You end up creating an endless loop.
The way I ended up doing it was by adding the class as one of its own type parameters. It's not too pretty, but it works.
public abstract class Saver<TSaver, TData>
where TSaver : Saver<TSaver, TData>
where TData : ISaveable<TData, TSaver>
{ ... }
public interface ISaveable<TData, TSaver>
where TData : ISaveable<TData, TSaver>
where TSaver : Saver<TSaver, TData>
{ ... }
public class WorkspaceWindow : ScalingWindow, ISaveable<WorkspaceWindow, WorkspaceWindowSaver>
{ ... }
public class WorkspaceWindowSaver : Saver<WorkspaceWindowSaver, WorkspaceWindow>
{ ... }
This is possible, the following is based on the answer to this question.
public class ClassA<BT, AT> :
where BT : ClassB<AT, BT>
where AT : ClassA<BT, AT>
{
BT btvar;
}
public class ClassB<AT, BT> :
where BT : ClassB<AT, BT>
where AT : ClassA<BT, AT>
{
AT atvar;
}
You won't be able to use the classes directly, you'll need to override them.
public ClassAImp : ClassA<ClassBImp, ClassAImp>
public ClassBImp : ClassB<ClassAImp, ClassBImp>
So you may as well make ClassA and ClassB abstract.
this will compile, but I would like to see you instantiate either ClassA or ClassB:
public class ClassA<TBt> where TBt : ClassB<TBt>
{
TBt _btvar;
}
public class ClassB<TAt> : ClassA<TAt> where TAt : ClassB<TAt>
{
TAt _atvar;
}
"Why would you want to?" sounds like a good question to me. The point of Generics it to allow you to abstract a class to allow it to use multiple types. If the constraint limits the type to a concrete type, you are only allowing the type and its subclasses. If you aren't doing this for subclasses, don't use generics. If you are, how about using an interface?
public interface IClassA<ITB> { }
public interface IClassB<ITA> { }
public class ClassA<AT,BT> : IClassA<BT> where BT : IClassB<AT>
{
BT btvar;
}
public class ClassB<BT,AT> : IClassB<AT> where AT : IClassA<BT>
{
AT atvar;
}
public class ClassADerivedClosed : ClassA<ClassADerivedClosed, ClassBDerivedClosed> { }
public class ClassBDerivedClosed : ClassB<ClassBDerivedClosed, ClassADerivedClosed> { }
Related
Let's say I have a generic class
public class G<T> {
}
and I want to declare final class as
public class F<T> where T : G<T> {
}
that seems to be possible, but what if I want to complicate the task and add a constraint to the class G like that
public class A {
}
public class DA : A {
}
public class DB : A {
}
public class G<T> where T : A {
}
and finally I want to do this
public class F<T> where T : G<T> {
}
that does not work, it says T has to be of type A which is understandable and it looks like I can rewrite it like this
public class F<T, U> where T : G<U> where U : A {
}
but in this case the usage has a redundant declaration
public class DF : F<G<DA>, DA> {
}
I have to repeat DA twice when using class F even though it is quite clear if I use G as a generic type the generic type of G is DA. Is there a way to avoid this redundancy?
And finally I want to do this
public class F<T> where T : G<T> {
}
But this doesn't seem to make much sense. You want F to be parametrized by T which must be G<X> where X is T again, which is G<X>, where X is T again and so on... This is a recursive definition.
I think what you actually want to define is something like (all examples are invalid C#)
public class F<T1<T2>> where T1 : G<T2> where T2 : A {
}
or maybe
public class F<T1> where T1 : G<T2> where T2 : A {
}
or maybe
public class F<G<T>> where T : A {
}
Those kind of "generics of generics" are called higher-kinded types, but C#, unfortunately, does not support them. Haskell does for instance and some other functional languages do too.
There is a C# library that mimics HKTs and it uses the workaround you found: specifying the inner type twice.
I have an interface :
public interface ICloneable<out T>
where T : ICloneable<T>
{
T Clone();
}
that should receive a type that implement this interface (as shown below).
And I can create a class that implement it :
public class Class : ICloneable<Class>
{
public Class Clone() { return (Class)MemberwiseClone(); }
}
Great !
But anyone can create a class that implement ICloneable<T> "wrong".
Does exist a way to prevent inheritance as shown below ? (2 examples)
public class Other : ICloneable<Class>
{
public Class Clone() { return new Class(); }
}
public class Other : Class, ICloneable<Class>
{
public Class Clone() { return (Other)MemberwiseClone(); }
}
And allow inheritance as shown below ? (any from 2 examples)
public class Other : ICloneable<Other>
{
public Other Clone() { return (Other)MemberwiseClone(); }
}
public class Other : Class, ICloneable<Other>
{
public Other Clone() { return (Other)MemberwiseClone(); }
}
You cannot overload a class, so:
public class Other : Class {}
public class Other : Class, IC<Other> {}
Will never work.
Now, I'm gonna pull a Jon Skeet and show how you could do this, but then discourage you from doing it. You could do something like this:
public class CloneableOther : Class, ICloneable<Other> { }
public class Other : CloneableOther
{
}
public class CloneableFoo : Class, ICloneable<Foo> { }
public class Foo : CloneableFoo
{
}
What this code is doing is effectively removing the generic parameter from the inheritance. Except, Foo can still do this: Foo : CloneableFoo, ICloneable<Other>, and now you'll have to create two classes for every ICloneable instance.
This goes into that why do you need this in the first place? It is a practice to do Foo : IInterface<Foo>, but there's no way to enforce it. Your best bet is to just do copy and paste and just be sure that the class matches.
Maybe another way is to have in the constructor of Class, a check to see if the type of ICloneable is the type of the class, and to throw an exception if it isn't, and that could sort've feel like a compile time error, if it's done earlier enough in the runtime.
I was using generic types in C# and I am new to using generic types. So, right now I am stuck with a problem. I have some classes like these:
public class MyModel1
{
}
public class MyModel2
{
}
public class BaseClass<T>
{
}
public class ChildClass1 : BaseClass<MyModel1>
{
}
public class ChildClass2 : BaseClass<MyModel2>
{
}
public class AnotherClass
{
//What will be the syntax of declaring this method
//The syntax of the following method is wrong and incomplete.
//It's there just to give an idea about whai i want to do.
public void MyMethod<T>()
where T : BaseClass<..what to write..>
{
}
}
My question is what will be the correct syntax of declaring MyMethod if I want to call MyMethod like this:
MyMethod<ChildClass1>();
If I understood correctly, you try to filter "MyMethod" so that T is a class of type "ChildClass ...".
You can add a generic parameter to your function like this:
public void MyMethod<T, U>()
where T : BaseClass<U>
{
}
But then you have to call MyMethod in that way.
MyMethod<ChildClass1, MyModel1>();
So it's quite complicated to use.
Another solution is to create a new "blank" class :
public abstract class Base // mark it as abstract if you don't need to use it in your code
{
}
public class MyModel1
{
}
public class MyModel2
{
}
public class BaseClass<T> : Base //The class inherits the new class
{
}
public class ChildClass1 : BaseClass<MyModel1>
{
}
public class ChildClass2 : BaseClass<MyModel2>
{
}
public class AnotherClass
{
public void MyMethod<T>()
where T : Base
{
}
}
You've forgotten to mention the return type and adding <T> after the class name. For example, if the return type is void, you could declare the method as:
public void MyMethod<T>()
where T : BaseClass<T>
{
}
This will work (by which I mean it compiles)
public void MyMethod<T>()
where T : BaseClass<MyModel1>
{ }
so does this:
public void MyMethod<T>()
where T : ChildClass1
{ }
Further edit after reading your comment...
You can do this:
public class AnotherClass<TBaseClass, TModel> where TBaseClass : BaseClass<TModel>
{
public void MyMethod(TBaseClass input)
{ }
}
I have a term for this, hopefully non-offensive. I call it The Generic Rabbit Hole of Madness. It's what happens when we try to combine generics and inheritance so that one set of classes can accomplish a broad set of goals that become increasingly confusing, and we solve it by adding more generic parameters and more generic classes.
You reach the bottom of the hole if you
- use <dynamic>
- check to see what the actual type is using GetType(), typeof, or is
- get it to compile but can't remember what it's supposed to do
I have the following classes and method:
public class MyGenericClass<T>
where T : class
{
}
public class MyClass
{
public TGen MyMethod<TGen>(TGen myGenClass)
where TGen : MyGenericClass<T>
where T : class
{
return myGenClass;
}
}
However, this gives an error because it cannot resolve the symbol T in MyMethod. I would prefer to not have to have MyMethod<TGen, T> since it seems a bit redundant to me. Is this possible?
You have to specify T before you can use it in a definition. There is no way for the compiler to know what T is.
So you should specify T before you use it (at method level as below, or perhaps at class level with MyClass):
public class MyClass
{
public TGen MyMethod<TGen, T>(TGen myGenClass)
where TGen : MyGenericClass<T>
where T : class
{
return myGenClass;
}
}
You can also use a concrete implementation of the generic type in the where clause:
public class MyClass
{
public TGen MyMethod<TGen>(TGen myGenClass)
where TGen : MyGenericClass<DateTime>
{
return myGenClass;
}
}
If you want to be able to use any MyGenericClass implementation for your TGen type, then you will need to create a base class of the MyGenericClass implementation to use (of course, this limits what functionality you will get for your TGen instance.
public class MyGenericClassBase { }
public class MyGenericClass<T> : MyGenericClassBase { }
public class MyClass<TGen>
where TGen: MyGenericClassBase
{
// Stuff
}
Sounds like you're just forgetting to include T in the list of generic types for the method:
public TGen MyMethod<TGen, T>(TGen myGenClass)
where TGen : MyGenericClass<T>
where T : class
{
return myGenClass;
}
I have the following situation below. This code will throw a compiler error for Test2
The type 'InheritedChild' cannot be used as type parameter 'T' in the generic type or method 'panelGenericIOGrid'. There is no implicit reference conversion from 'InheritedChild' to 'SerializerBase'.
public class SerializerBase<T>
{
}
public class DirectChild : SerializerBase<DirectChild>
{
}
public class InheritedChild : DirectChild
{
}
public class panelGenericIOGrid<T> : UserControl
where T: SerializerBase<T>, new()
{
}
...
panelGenericIOGrid<DirectChild> test;
panelGenericIOGrid<InheritedChild> test2;
...
I'm pretty convinced my implentation is funadmentally wrong. I want the following situation, both DirectChild and InheritedChild will give their appropriote type to the SerializerBase constuctor.
How do I get the code to work the way it needs to? Thanks!
Some info on the actual information. SerializerBase has a set of static functions that are implemented to automatically serialize and deserialize themselves based on their type.
DirectChild has a set of strings that are going to be stored on disk and recovered.
Inhertiedchild has all the members of DirectChild plus more.
Basically I'm going to need DirectChild.Serialize(filename), and IndirectChild.Serialize(filename), where the Serialize is a public member of SerializeBase
The problem is that InheritedChild doesn't implement SerializerBase<InheritedChild>, so it doesn't fulfil the constraints for T in panelGenericIOGrid<T>.
Unfortunately it's not clear that the solution is meant to be as we don't know what you're trying to achieve.
What are the members of SerializerBase<T> in real life? If you could give us more context, it would help us to help you.
It seems to me that you're missing an interface:
public interface ISerializerBase<T> { }
public class SerializerBase<T> : ISerializerBase<T> { }
public class DirectChild : SerializerBase<DirectChild> { }
public class InheritedChild : DirectChild, ISerializerBase<InheritedChild> { }
public class panelGenericIOGrid<T> where T: ISerializerBase<T>, new() { }
I don't know how that will change your design though. It might be that you'll need to reimplement some inherited methods or some interface methods in the InheritedChild.
But, maybe you can do this otherwise:
public interface MSerializable {}
public static class Serializable {
public static void Serialize(this MSerializable self, string fileName) {
// self will refer to the right type,
// no need to use generics if all you want is to serialize it ...
}
}
public class DirectChild : MSerializable { }
public class InheritedChild : DirectChild { }
public class panelGenericIOGrid<T> where T: MSerializable, new() { }
Will you do a binary serialization, or will you serialize it to XML?