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
Related
I have got two abstract classes: Particle and ParticleHub<T> where T : Particle.
I want, when I inherit Particle, for its constructor to require a ParticleHub of its respective type to be passed as a parameter. I want its constructor to be like this:
ParticleHub</*type of the inheriting class*/> _particleHub;
public Particle(ParticleHub</*type of the inheriting class*/> particleHub, OtherParameters otherParameters)
{
_particleHub = particleHub;
//use other parameters
}
If I write ParticleHub<typeof(this)> I get overloaded with errors that make me think I broke the syntax for the rest of the file. If I do ParticleHub<this.GetType()> I get the same problem, but only with fewer errors. Though with that one, I can understand that I can't use a function in a parameter type like that.
So how do I go about this?
Simple example below of an interface and generic pattern
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
IChair chair = new Chesterfield<Arm>(new Arm());
chair.Sit();
Console.Write(chair.HasSat());
}
}
public interface IChair
{
void Sit();
bool HasSat();
}
public interface IPart
{
}
public class Chesterfield<TPart> : Chair<TPart> where TPart : IPart
{
public Chesterfield(TPart part) => _part = part;
private bool _hasSat;
private readonly TPart _part;
public override void Sit()
{
_hasSat = true;
}
public override bool HasSat() => _hasSat;
}
public abstract class Chair<TPart> : IChair where TPart : IPart
{
public abstract void Sit();
public abstract bool HasSat();
}
public class Arm : IPart
{
}
public class Back : IPart
{
}
public class Leg
{
}
You have no other choise i think but to pass the type as other generic. See the exaple how it is done in terms of fluent builders as I think it is very close what You want to have: Link.
Like:
FePatricle<T, U> where T: Particle, U: FeParticle<T, U>
Looks crazy but it should do the trick in the constructor:
public FeParticle(ParticleHub<U>...
EDIT:
Or more likely as far as I understand:
FePatricle<T>: Particle where T: FeParticle<T>
So FePartivle extends Particle and it carries with itself its own type T?
Passing the inherited class itself, as below.
public abstract class Particle
{
}
public abstract class ParticleHub<T> where T : Particle
{
}
public class k1 : Particle
{
ParticleHub<k1> _particleHub = null;
public k1(ParticleHub<k1> ph)
{
_particleHub = ph;
}
}
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 have the following Classes:
public abstract class Gear<T> : ScriptableObject, IGear { ... }
public class Armor : Gear<ArmorStatsLevel> { ... }
public class Weapon : Gear<WeaponStatsLevel> { ... }
Now I had the following methods to list my instances:
public abstract class WidgetListArmor {
public void ActionSelected(Armor gear) {
if (...) GameSession.Equip(gear);
}
}
public abstract class WidgetListWeapon {
public void ActionSelected(Weapon gear) {
if (...) GameSession.Equip(gear);
}
}
Because this was kind of redundant, I thought of moving it all to a base clase:
public abstract class WidgetListGear<T> : MonoBehaviour {
public void ActionSelected(T gear) {
if (...) GameSession.Equip(gear);
}
}
public class WidgetListArmors : WidgetListGear<Armor> { ... }
public class WidgetListWeapons : WidgetListGear<Weapon> { ... }
And while this seems cleaner, I have a new problem now. Because T is a Generic, GameSession.Equip can't overload gear.
Did I chose a bad pattern to organize my code? Am I missing something from Generics that allows me to do this operation?
UPDATE
Here is the GameSession signatures:
public class GameSession {
public static bool Equip(Armor armor);
public static bool Equip(Weapon weapon);
}
Make Weapon and Armor implement an interface called IGear, for example:
public interface IGear
{ }
public class Weapon : IGear
{
//snip
}
public class Armor : IGear
{
//snip
}
Constrain the generic type to IGear:
public abstract class WidgetListGear<T> : MonoBehaviour
where T : IGear
{
public void ActionSelected(T gear) {
if (...) GameSession.Equip(gear);
}
}
And make GameSession.Equip take IGear as the parameter type.
What you're looking for is dynamic dispatch. I would suggest you try the following:
GameSession.Equip((dynamic)gear);
However, I don't think it's the best idea since you've tried to encode your Game rules in type system and right now you're starting a mini-compiler in runtime to perform a dispatch for you.
I'd like to point you to Eric Lippert's articles on that subject. Looks like you have similar issues with what he's described.
Part 4 describes the dynamic approach I've provided as well as its disadvantages. Part 5 provides a completely different approach. Overall, I highly recommend reading each part.
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 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> { }