-3

I need to extend a class where the child class will not use some members of the parent class. How should the unused members be handled and still respect the open/closed SOLID principle? I'm considering doing something like the code below, but existing code that uses reflection will get exceptions. If I put data in the deprecated members, existing code will produce unexpected results.

I feel that a totally different approach should probably be used. How to handle this situation without modifying existing code (which would also violate the open/closed principle)?

class Parent
{
    public virtual int myVarMin { get; set; } = 0;
    public virtual int myVarMax { get; set; } = 10;
    public int myVar { get; set; }
    public int unchanged1 {get; set;}
    //.
    //numerous other members that are irrelevant to the question
    //.
    public void doSomething(){/*do something*/}
    //
}

class Child:Parent
{
    //considered impementation from original question 
    public override int myVarMin => throw new NotSupportedException();
    public override int myVarMax => throw new NotSupportedException();
    public List<int> myVarList = new List<int>();
}

class MyExistingCode
{
    public void Display(Parent parent)
    {
        foreach (var info in parent.GetType().GetProperties())
        {
            Console.WriteLine($"{info.Name}: {info.GetValue(parent)}");
        }
    }
}
Kevin S. Miller
  • 913
  • 1
  • 9
  • 21
  • Reading that it doesn't look like a `Child` is a particular sort of `Parent` at all; not sure inheritance is what you want here. – AakashM Jan 29 '21 at 14:20
  • 4
    Sounds like you'll be violating the Liskov substitution principle that says that wherever you'd use a `Parent` you should be able to use a `Child` without anything breaking. – Hans Kilian Jan 29 '21 at 14:25
  • 2
    I'm with @HansKilian on this one. It sounds like you shouldn't be describing the problem using Generic names, maybe well be able to fix your architecture if you describe the problem – johnny 5 Jan 29 '21 at 14:57
  • @AakashM, this is just an example. There are many other members in the real project I'm working on and I just included what is needed to focus on the issue of the question. – Kevin S. Miller Jan 30 '21 at 18:05
  • @johnny 5, I cannot re-architect the system without violating the open/closed principle. The system has been in use for years and the existing code cannot be changed. That is the whole nature of this question. – Kevin S. Miller Jan 30 '21 at 18:11
  • @HansKilian, Liskov violation is a consideration. A Child is still technically a Parent and I am surprised that int? could be substituted or int and the compiler gave no warning or error. I think it will be OK because MyNewCode (see my answer) will replace MyExistingCode in this new version. I am in the process of implementing my answer now, but it is a very large project (2 MSVS Solutions and 65 projects) and will take a while. – Kevin S. Miller Jan 30 '21 at 18:22
  • This question is about concepts - not about the classes I used as an example. – Kevin S. Miller Jan 30 '21 at 18:36

2 Answers2

0

Use the Obsolete attribute to inform the developers that your method is deprecated and they should use the new version.

[Obsolete("Method is deprecated, use Method2 please.")]
public void Method()
{ 
    … 
}

Here I've changed your code, so using reflection you can detect whether a method/property is deprecated or not, and it'll not throw an exception anymore.

    public class Parent
    {
        public virtual int myVarMin { get; set; } = 0;
        public virtual int myVarMax { get; set; } = 10;
        public int myVar { get; set; }
    }

    public class Child : Parent
    {
        [Obsolete("Use other property")]
        public override int myVarMin => throw new NotSupportedException();
        [Obsolete("Use other property")]
        public override int myVarMax => throw new NotSupportedException();
        public List<int> myVarList = new List<int>();
    }

    class MyExistingCode
    {
        public void Display(Parent parent)
        {
            foreach (var info in parent.GetType().GetProperties())
            {
                var customeAttributes = (ObsoleteAttribute[])info.GetCustomAttributes(typeof(ObsoleteAttribute), false);
                if (customeAttributes.Length > 0)
                {
                    Console.WriteLine($"{info.Name} is deprecated.");
                }
                else
                {
                    Console.WriteLine($"{info.Name}: {info.GetValue(parent)}");
                }
            }
        }
    }
Saeed Aghdam
  • 307
  • 3
  • 11
  • 1
    I see how that technically works, but changing Parent and MyExistingCode violates the open/closed principle - right? – Kevin S. Miller Jan 29 '21 at 14:29
  • Sorry, I've put [Obsolete("Use other property")] in the wrong place, how is it if we put `Obsolute` attribute in the child class? We've to handle it in order to handle exceptions, am I right? – Saeed Aghdam Jan 29 '21 at 14:40
  • I think I will have to also extend MyExistingCode to avoid changing it. – Kevin S. Miller Jan 29 '21 at 15:42
-1

I ended up doing something like this (the Parent and MyExistingCode classes were unchanged, so they comply with the open/closed principle):

class Child : Parent
    {
        public new int? myVarMin => null;
        public new int? myVarMax => null;
        public List<int> myVarList = new List<int>();
    }

 class MyNewCode : MyExistingCode
    {
        public new void Display(Parent parent)
        {
            foreach (var info in parent.GetType().GetProperties())
            {
                Console.WriteLine($"{info.Name}: {info.GetValue(parent) ?? "NULL"}");
            }
        }
    }

I was surprised that I could hide an int with an int? without an error. I will Accept another answer if it is better.

Kevin S. Miller
  • 913
  • 1
  • 9
  • 21