1

I am just practicing some random coding principles mostly the Liskov Substitution Principle from the SOLID principles...

Anyway I was experimenting with random scenarios to help deepen my understanding and I ran into an issue that will make more sense after viewing the following code.

In a class I've called Item:

public virtual bool CheckIfMeetStats(IHasStats hasStats)
{
    if (_hasStatRequirement)
    {
        if (hasStats.Strength >= _strengthRequirement && hasStats.Level >= _levelRequirement)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return true;
    }
}

Which requires an IHasStats as an argument, then in a class that inherits from Item when I try to override it I try to pass an interface as an argument that inherits from IHasStats:

public interface IHaveMagic : IHasStats
{
    public int CosmicPower { get; set; }
    public int Mana { get; set; }
}

Into EDIT: (To make it more clear I have put the parameters I'm trying to pass here, originally I kept it to what works):

public override bool CheckIfMeetStats(IHaveMagic haveMagic)
    {
        return base.CheckIfMeetStats(haveMagic);
    }

If I try to pass IHaveMagic as an argument it doesn't work it says suitable method not found. I don't understand why because of it being a descendent of IHasStats.

Can someone please explain why and recommend what they would do to still follow SOLID principles and be able to check if a character meets the stats requirements for an item.

I'm still getting used to Stack overflow whoever edited and closed my question because they said its similar to a different post please describe why because I don't see how it answers my question or at the very least its VERY confusing and unclear...

adam
  • 65
  • 7
  • 1
    Where is the code where you supposedly pass an `IHaveMagic` instance? Because that indeed should work. If your question is "why can't I make `CheckIfMeetStats` take an `IHaveMagic` instead of an `IHasStats`", that's because you have to stick to the original contract of accepting any `IHasStats`; you're not allowed to narrow your scope in the override (because then a call through `Item` might fail). – Jeroen Mostert Feb 18 '22 at 20:10
  • If you want help with a scenario that doesn't work you need to also include the code that doesn't work. From the description it's not clear what scenario fails. – V0ldek Feb 18 '22 at 20:18
  • @JeroenMostert thanks for your response, I want to pass it in the CheckIfMeetStats in the MagicalStaff class. I thought it should work because IhaveMagic inherits from IHasStats. So it has all of the same properties plus some extra so I don't get how it would fail when it has all of the same properties. – adam Feb 18 '22 at 20:20
  • @V0ldek I've edited it now in the area with what I'm trying to do that doesn't work – adam Feb 18 '22 at 20:24
  • 1
    Consider the following (legal) code: `IHasStats thingWithStats = new SomeThingWithStats(); Item i = new MagicalStaff(); i.CheckIfMeetStats(thingWithStats)`. For this to work, it must be the case that `MagicalStafff.CheckIfMeetStats` takes an `IHasStats` per the contract of the base class, not a more restrictive `IHaveMagic`, since we cannot guarantee the argument is an `IHaveMagic`. Now if the method *returned* an `IHasStats`, it would be legal to return an `IHaveMagic` instead, since now you're giving *more* than is asked for. This is covariance and contravariance at work. – Jeroen Mostert Feb 18 '22 at 20:25
  • There's undoubtedly a beautiful question on Stack Overflow somewhere that explains this and would serve as a duplicate, but I'm too lazy to search for it. – Jeroen Mostert Feb 18 '22 at 20:26
  • @JeroenMostert I thought I was *too lazy* to type up long comment and searched for duplicate instead :) – Alexei Levenkov Feb 18 '22 at 20:30
  • @AlexeiLevenkov: That's actually not a good dupe, because it's talking about return type covariance specifically, whereas this is about argument type covariance. Return type covariance actually is part of C# these days, *argument* type covariance of course is not since it's not safe (only argument type contravariance). – Jeroen Mostert Feb 18 '22 at 20:32
  • @JeroenMostert there are 2 answers one with "In the C# programming language, support for both return-type covariance and parameter contravariance for delegates was added in version 2.0 of the language. *Neither covariance nor contravariance are supported for method overriding*." and another "C#9 adds return covariance" - I think that is enough to cover... Also indeed one can spend more time and find even more specific one (and edit duplicate list). – Alexei Levenkov Feb 18 '22 at 20:36
  • Adam "I don't see how it {duplicate} answers my question or at the very least its VERY confusing and unclear" - you asked about one of the harderst to understand topics in C# (or languages in general) - co/contra-variance of parameters/return values. The duplicate I selected does answer your question (read all answers there), but I don't really expect that duplicate to be enough to understand the topic. You may want to search for "C# variance covariance contravariance" in your favorite search engine and read results for some time. Hopefully one of many would help you to understand that... – Alexei Levenkov Feb 18 '22 at 20:42
  • ...topic. Again - it is quite complex topic (I'd rate it above "understand recursion for the first time" for example). @JeroenMostert tried to explain it in a comment above - while I don't think it would be enough for me when I first encountered problems with variance it may be all *you* need to grasp variance. – Alexei Levenkov Feb 18 '22 at 20:45
  • 1
    [Another question tackling largely the same subject matter](https://stackoverflow.com/q/17944322/4137916) (in the context of a game, even), it might be helpful. – Jeroen Mostert Feb 18 '22 at 20:49
  • Thank you for your responses, I had never even heard of contra/covariance until asking this question. I am currently still looking into these concepts now. I've looked at a few blog posts and YT videos and I haven't yet come across one resource that clearly breaks down each part of what this concept is so I still don't understand the answer to my question. I will edit my question with the answer in the future if I discover and understand the answer to it. – adam Feb 20 '22 at 15:56

0 Answers0