Generic covariance workarounds? - c#

I have bullets:
public LaserBullets : Projectile
I have a gun that inherits from a base weapon with the bullet type that goes with the derived gun:
public LaserWeapon : Weapon<LaserBullets>
I have a property to establish the current weapon of a player:
public Weapon<Projectile> CurrentWeapon
{
set { currentWeapon = value; }
}
This fails (cannot implicitly cast LaserWeapon to Weapon<Projectile>)
player.CurrentWeapon = new LaserWeapon();
I've read about no support for certain covariance in C#, but I don't want to think that I'm stuck here. Is there any way to make my situation work? I'm trying to maintain a base weapon in the player class but work with a more specific weapon in another class.
I tried creating my own cast operator for LaserWeapon but I got an error about being unable to cast to/from base class.

I believe your example would have worked if Weapon<T> was a covariant interface declared like so:
public interface Weapon<out TProjectile>
{
}
This will only work where TProjectile is used as a return value, e.g:
TProjectile Fire();
But will NOT work where TProjectile is used as an input parameter, e.g:
LoadClip(IEnumerable<TProjectile> ammo);
From your comments, I can see you're after the generic return value, so make an interface as follows:
public interface IWeapon<out TProjectile> where TProjectile : Projectile
{
TProjectile Fire();
}
Then use this interface implementation; There is nothing stopping you from having a useful base class, e.g.
public abstract class Weapon<TProjectile> : IWeapon<TProjectile> where TProjectile : Projectile
{
public abstract TProjectile Fire();
}
As long as your player class is along the lines of:
public class Player
{
public IWeapon<Projectile> CurrentWeapon { get; set; }
}
This allows you to still use CurrentWeapon.Fire() which will return an instance of Projectile.

Make a non-generic class Weapon that Weapon<T> inherits. Use that class instead of Weapon<Projectile> in your code.

Related

In a method that takes an interface, how could I make the method take any interface's derived interface?

So I have an interface relationship like this:
public interface IPlayer {
public List<Card> LoseCards();
}
public interface IPlayer_WithPersonalDeck : IPlayer {
public void AddCardsToPersonalDeck(List<Card>);
}
I also have a generic command interface that is inherited to command players
public interface ICommands<T> {
public Object? execute (T)
public Object? execute (T, T);
}
public class BasePlayerCommands : ICommands<IPlayer> {
public virtual Object? execute (IPlayer) { throw new NotSupportedException(); }
public virtual Object? execute (IPlayer executor, IPlayer target) { throw new NotSupportedException(); }
}
I want to do something like this:
public class TakeCardsFromPlayer_AndAddToDeckCommand : BasePlayerCommands {
public override List<Card> execute(IPlayer_WithPersonalDeck executor, IPlayer target) {
return executor.AddCardsToPersonalDeck(target.LoseCards());
}
}
This does not work as TakeCardsFromPlayer_AndAddToDeckCommand does not find a suitable execute method to overload. Obviously this is because I am using IPlayer_WithPersonalDeck instead IPlayer. However, since that interface derives from IPlayer, why can't I substitute it in?
Doesn't this follow the Liskov Principle, since any class that implements IPlayer_WithPersonalDeck means they are also an IPlayer?
So to me, execute() shouldn't care that it's a derived interface since it shares a base interface of IPlayer like it requires.
Any explanation as to why this doesn't work as I thought would be appreciated, as well as how I can work around this problem.
I would like to avoid having to implement a class that uses ICommand<IPlayer_WithPersonalDeck> as not all my commands may need both players to have a deck, if that's possible.
A solution I have right now is to cast an IPlayer to the correct interface inside of execute. Its unsafe though as any IPlayer could be passed to it and can throw errors if the user isn't careful as not every player has a deck.

Diffrent types for one variable

I want to set gunS to Shotgun type or AR15 type but dont know how and this code dont work
public Shotgun gunS2;
public AR15 gunS3;
public MonoBehaviour gunS;
private void Start()
{
set();
}
public void set()
{
if(gunT.name == "Shotgun")
{
gunS = gunT.GetComponent<Shotgun>();
}
else
{
gunS = gunT.GetComponent<AR15>();
}
}
Give them a common interface.
interface IWeapon
{
}
class AR15 : IWeapon
{
}
class Shotgun : IWeapon
{
}
If your classes are defined this way (you obviously have to add implementation) then you can write a variable that can contain any IWeapon.
public IWeapon gunS;
public void set()
{
if(gunT.name == "Shotgun")
{
gunS = gunT.GetComponent<Shotgun>();
}
else
{
gunS = gunT.GetComponent<AR15>();
}
}
Either use a base class and/or interface
public interface IWeapon
{
// whatever public properties and methods shall be accessible through this interface
}
public abstract class Weapon : MonoBehaviour //, IWeapon
{
// whatever fields, properties and methods are shared between all subtypes
// if using the interface implementation of it
}
and then
public class ShotGun : Weapon
// or if for some reason you don't want a common base class
//public class ShotGun : MonoBehaviour, IWeapon
{
// whatever additional or override fields, properties and methods this needs
// or if using the interface the implementation of it
}
and
public class AK74 : Weapon
// or if for some reason you don't want a common base class
//public class AK74 : MonoBehaviour, IWeapon
{
// whatever additional or override fields, properties and methods this needs
// or if using the interface the implementation of it
}
Then simply drag the according object/component into the exposed slot in the Inspector in Unity.
There is no need for your gunT field (wherever it comes from)
// Already reference this via the Inspector in Unity
// then you don't need your Start/set method AT ALL!
public Weapon gunS;
If for some reason this is not an option e.g. if using only the interface
public IWeapon gunS;
there still is no need to check the name or specify the type further. GetComponent will return the first encountered component of the given type or a type inheriting from it. You can simply do
void Awake()
{
// as fallback if for whatever reason you can't directly reference it via the Inspector
// (which doesn't seem to be the case since somewhere you get gunT from ...)
if(!gunS) gunS = /*Wherever this comes from*/ gunT.GetComponent<Weapon>();
// or i only using the interface
//if(!gunS) gunS = /*Wherever this comes from*/ gunT.GetComponent<IWeapon>();
}

How can I get the derived primitive type from a generic interface?

Been struggling with this for a bit so wanted to see if someone here might know the solution. I have a set of interfaces (and an implementation):
public interface IInputValue
{
}
public interface IInputValue<T> : IInputValue where T : struct
{
T Value { get; set; }
}
public class ButtonInputValue : IInputValue<bool>
{
private bool m_Value;
public bool Value
{
get => m_Value;
set => m_Value = value;
}
}
And then I have a scriptable object class that inherits and uses implementations of IInputValue<T>.
public abstract class InputTrigger : ScriptableObject
{
}
public abstract class InputTrigger<T> : InputTrigger where T : IInputValue
{
public abstract T InputValue { get; }
}
T for example, could be ButtonInputValue. I want to serialize this scriptable object in another class (as InputTrigger) and simply be able to call .InputValue on it to get the correct derived type of IInputValue or just get the primitive value directly through IInputValue<T>.Value. What's the best way to go about doing this? Any help is much appreciated.
I think you are trying to use interfaces in the wrong way.
The purpose of an interface is to abstract common functionality on potentially completely different objects and not caring about the base class that implements the interface.
For instance:
You can have a car object and a electric car objects and they can both accelerate and brake. They do so in different ways but all you need to know is that they can accelerate and brake using the pedals (the interface), without you going to learn to drive a car once again.
To answer your question, I think you can use the interface as a type and call the method on the object, and in case you need to verify it is a certain type that implements that interface you can cast to it.
IInputValue<bool> myInputValue;
(ButtonInputValue) myInputValue; // This will be treated as ButtonInputValue because the class implements the IInputValue interface.

Cannot convert type `ObjectPool<T>' to `ObjectPool<ObjectPoolObject>'

I have a simple ObjectPool implementation. The ObjectPool holds a stack of ObjectPoolObjects. ObjectPoolObject is an abstract class that defines two methods. I am trying to add a reference to the parent pool to the ObjectPoolObject class. I got to work by using the curiously recurring template pattern, but then i couldnt derive from a class that was already deriving from ObjectPoolObject. (ie, grenade inherits from bullet which inherits from ObjectPoolObject)...
So anyways, the parent pool is held by the ObjectPoolObject like so: ObjectPool m_parent... but I cant seem to set m_parent = this; in the ObjectPool class... It seems like I definetly should be able to. I dont want to have to cast to object and then cast to ObjectPool.. it seems like I should safely be able to do this:
public abstract class ObjectPoolObject : MonoBehaviour {
public abstract void ObjectPool_Activate();
public abstract void ObjectPool_Deactivate();
public ObjectPool<ObjectPoolObject> m_pool;
}
public class ObjectPool<T> where T : ObjectPoolObject
{
public ObjectPool(CreateObjectDelegate creationMethod)
{
m_creationMethod = creationMethod;
T objectPoolObject = creationMethod();
// this is the line that gives me error CS0030: Cannot convert type `ObjectPool<T>' to `ObjectPool<ObjectPoolObject>'
objectPoolObject.m_pool = this;
}
}
Since T : ObjectPoolObject I should be able to do this...
All I need is for the ObjectPoolObject to have a reference to it's parent ObjectPool... how can I accomplish this?
EDIT:
This is the error message:
error CS0030: Cannot convert type ObjectPool<T> to ObjectPool<ObjectPoolObject>'
but since T: ObjectPoolObject it seems like it should be able to do this. its like it's saying "cannot convert a child class to a parent class..." T inherits from ObjectPoolObject... thus ObjectPool of T is analogous to a child class ObjectPool of ObjectPoolObject... Liskov substitution principle should allow me to cast a ObjectPool of T to an ObjectPool of ObjectPoolObject.
For example, I have an ObjectPool of Bullets where Bullet inherits from ObjectPoolObject. I should be able to cast it to an ObjectPool of ObjectPoolObjects, considering all the elements in the ObjectPool are ObjectPoolObjects.....
EDIT - I'm starting to understand what the problem with the cast is.... its so hard to explain, wow.
EDIT2 - The answer of defining an interface using the "in" keyword on the generic type is the correct answer for this problem. HOWEVER! I have decided that this has crossed the line of "too complex" and have decided to remove the m_pool field from the ObjectPoolObject. When you get an object from an object pool, its up to you to record which pool it came from and put it back appropriately. The m_pool field was simply dirtying up my system too much for me to justify including it.
Removing the Genericism from ObjectPool completely is also a functional solution, but it requires me to cast the return result of every ObjectPool.Get call, and I decided I didn't want that either.
You need to make your type parameter contravariant using the in modifier, which is only allowed on interfaces and delegates.
interface IObjectPool<in T> where T : ObjectPoolObject
{
}
class ObjectPoolObject
{
public IObjectPool<ObjectPoolObject> Pool { get; internal set; }
}
class ObjectPool<T> : IObjectPool<T> where T : ObjectPoolObject
{
public ObjectPool(Func<T> createObject)
{
T obj = createObject();
obj.Pool = this;
}
}
You need to look at covariance and contravariance in C# as documented here.
In short - you need to declare your class as
public class ObjectPool<**in** T> where T : ObjectPoolObject
The problem is that while T does indeed inherit from ObjectPoolObject, that hos nothing to do wether ObjectPool<T> inherits from ObjectPool<ObjectPoolObject>.
for example, take the following code:
public class Class1<T> where T : Class3, new()
{
public Class1()
{
Class3 variable1;
variable1 = new T(); // This works just fine T is or inherits from Class3
Class1<Class3> variable3;
variable3 = new Class1<Class2>(); // This will not work, while Class2 does indeed inherit from Class3,
// Class1<Class2> is still a different class from Class1<Class3>
// while their type parameters have an inheritance between them, they themselves do not.
Class1<Class3> variable2;
variable2 = new Class1<T>(); // And for just the same reason as stated above, this will not work either
}
}
To do what you're trying to do, we would have to make something more complex (and to be honest, complexity is not Always a good thing). But just for the sake of it, have a look at this:
public class AbstractObjectPool<T, T2> where T : AbstractObjectPool<T, T2> where T2 : ObjectPoolObject<T, T2>
{
public T m_pool;
}
public class ObjectPool<T> : AbstractObjectPool<ObjectPool<T>, T> where T : ObjectPoolObject<ObjectPool<T>, T>
{
public ObjectPool(Func<ObjectPool<T>> creationMethod)
{
ObjectPool<T> objectPoolObject = creationMethod();
objectPoolObject.m_pool = this;
}
}
public abstract class ObjectPoolObject<T, T2> where T : AbstractObjectPool<T, T2> where T2 : ObjectPoolObject<T, T2>
{
}
However, I wonder if (in your case) you wouldn't be better off skipping generics alltogeather and co for a simple base class:
public class ObjectPool
{
public ObjectPool(Func<ObjectPoolObject> creationMethod)
{
ObjectPoolObject objectPoolObject = creationMethod();
objectPoolObject.m_pool = this;
}
}
public abstract class ObjectPoolObject
{
public ObjectPool m_pool;
}

Is it a right generalization association of an interface class

As the ttitle said, im asking if is this a good programmation/design way.
I got a class, that can be just an Interface (only got 1 abstract method and few attributes)
As a example of my case, this is similar:
We got a main class Car than could be a truck, auto, moto, ...
and has an abstract method void move()
Could I design CAR as interface, and the other concrete classes as a generalization of CAR class? or is this wrong?
public interface Car{
private int length;
private float speed;
public void move();
}
public class truck : Car{
//Constructor
public Car(int size)
{
length=size;
}
public void move()
{
//Move code
}
}
and then
Car myCar = new truck();
myCar.move();
Would be right?
You're mixing up the terms "abstract" and "interface" here.
It is perfectly fine to refer to an instance of a class by the interface it implements. Here you see an interface, ICookieFactory which bakes abstract Cookies:
public interface ICookieFactory
{
Cookie BakeCookie();
}
public class ChocolateChipCookieFactory : ICookieFactory
{
public Cookie BakeCookie()
{
return new ChocolateChipCookie();
}
}
public abstract class Cookie
{
public abstract IEnumerable<Crumb> Crumble();
}
public class ChocolateChipCookie : Cookie
{
public override IEnumerable<Crumb> Crumble()
{
...
}
}
ICookieFactory factory = new ChocolateChipCookieFactory();
Cookie cookie = factory.BakeCookie();
foreach (Crumb crumb in cookie.Crumble())
{
...
}
An interface tells implementations of it which methods or properties it must support, but cannot provide any implementation code itself. You can't define fields in an interface.
An abstract class can include any number of fields and methods, and abstract methods that must be overridden by child classes.
A single class can implement multiple interfaces but only inherit from a single abstract class.
As far as i Know, yes, its possible and right to create an interface which child classes would be a case of generalization association.
But in my case, and thanks to the only answer of C.Evenhuis, i realized it'll be better to make an abstract class (so i can combine some abstract methods that child classes must override, with some concrete methods that childs can override or simply use).

Categories

Resources