3

I try to give an example as simple as possible but the very essence of the question is quite confusing at least to me.

In order to reuse code and not repeat my self I have an interface IStudent that implements another interface IPerson.

interface IPerson
{
    string FirstName { get; set; }
    string LastName { get; set; }
}

interface IStudent : IPerson
{
    int StudentNumber { get; set; }
}

I have a class implementing IStudent

class Student : IStudent
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int StudentNumber { get; set; }
}

Now I have an abstract class CommunityBase that has a field of type IPerson

abstract class CommunityBase
{
    public IPerson member;

    public CommunityBase(IPerson member)
    {
        this.member = member;
    }

    public void ShowName()
    {
        Console.WriteLine(this.member.FirstName + " " + this.member.LastName);
    }
}

I also have a class derived from CommunityBase called University. Because University is a more concrete class I want to use a more concrete interface. So the code is the following:

class University : CommunityBase
{
    public new IStudent member;

    public University(IStudent member)
        : base(member)
    {
        this.member = member;
    }

    public void ShowStudentNumber()
    {
        Console.WriteLine(this.member.StudentNumber);
    }
}

In order for all this to work the new keyword should be declared for the IStudent member. Also I would need to assign the member field once in the base class and again in the derived class.

What I want to do is assign member once in the CommunityBase constructor but this way I cannot use the properties or methods defined in IStudent in the University class.

All of the above code compiles and works as expected but I was wondering if there is an easier and more simple/easy to read way to do this. From what I read people tend to avoid using the new keyword for hiding if I continue using this code would there be any potential issues?

dbc
  • 104,963
  • 20
  • 228
  • 340
Adrian Hristov
  • 1,957
  • 2
  • 16
  • 22
  • 1
    If you have "new" in your own code it essentially says that you can't agree with yourself on type which does not give a lot of confidence in your code... For issues with "new" - check out http://stackoverflow.com/questions/2784423/valid-applications-of-member-hiding-with-the-new-keyword-in-c-sharp and especially link to [Eric Lippert blog](http://blogs.msdn.com/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx) in the post. Otherwise [prefer composition over inheritance](http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance) is something to think about. – Alexei Levenkov Apr 08 '14 at 21:34

2 Answers2

2

Make your CommunityBase class generic

abstract class CommunityBase<T> where T : IPerson
{
    protected T _member;
    public T Member { get return _member; }

    public CommunityBase(T member)
    {
        _member = member;
    }

    public virtual void ShowName()
    {
        Console.WriteLine(_member.FirstName + " " + _member.LastName);
    }
}

Also make ShowName virtual, this enables more concrete classes to override it.

class University : CommunityBase<IStudent>
{
    public University(IStudent member)
        : base(member)
    {
    }

    public override void ShowName()
    {
        Console.WriteLine(_member.FirstName + " " + _member.LastName +
             " Student-No. = " + _member.StudentNumber);
    }
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • But what if `CommunityBase` has a few fields of type `IPerson`, and in `University` he wants to change them to more than one subtype, such as `IDean`, `IProfessor`, and so on? With this approach he would have to give a generic type for each field. – Daniel Apr 08 '14 at 21:57
  • This answer fully resolves my issues but @Daniel raises a good point. I guess the class would look something like CommunityBase which is not very pretty to look at. – Adrian Hristov Apr 08 '14 at 22:01
  • Since you have only one field, you can only have one type. But if the types you mention all implement `IPerson` you could create a type `TeachingStaff : CommunityBase` for instance. But you get the most flexibility by just having a `List _members;` in `CommunityBase` and implement person type specific things in the different person types themselves. I.e. `ShowName` would be a member of `IPerson`, not of `CommunityBase`. – Olivier Jacot-Descombes Apr 08 '14 at 22:02
  • @OlivierJacot-Descombes Could you elaborate? I don't understand how having a type `TechingStaff` solves the case where you have three different fields, each with a different subtype of `IPerson`. In response to your edit: I remain unconvinced that your solution is practical for a case in which `Community` has many different `IPerson` fields and in `University` you want to change each one to a specific `IPerson`: in this case, hiding with `new` is probably the best solution. Your solution is fine for this specific case, of course, in which there is only one field. – Daniel Apr 08 '14 at 22:04
  • @Daniel: See my previous comment. The idea is to drop the generic stuff and instead move all the stuff specific to different person groups to `IPerson`. Then have a list of `IPerson` in the `CommunityBase`, since communities will probably have more than one member. But since the OP had only one `member` field, I did not include this option in my answer. – Olivier Jacot-Descombes Apr 08 '14 at 22:10
  • @OlivierJacot-Descombes But with this solution, I can't have a field such as `IPerson Leader` in `Community`, and then have `Leader` be of type `IDean` in `University`. We would have to revert back to generics.. Which again raises the issue of what happens when we want to have multiple fields. – Daniel Apr 08 '14 at 22:15
  • You can have more than one generic type parameter. `class CommunityBase where TLeader : IPerson where TMember : IPerson` – Olivier Jacot-Descombes Apr 08 '14 at 22:18
  • @OlivierJacot-Descombes Yes, and if we were to have 100 fields, we would need 100 generic types. I find this impractical. – Daniel Apr 08 '14 at 22:19
  • I really don't see your point. If having 100 fields is impractical why do you want to have 100 fields? And why do they have to be generic? Just make hard typed fields having different names in derived classes that suit your needs. You won't get any conflicts like this. – Olivier Jacot-Descombes Apr 08 '14 at 22:24
  • Having 100 fields isn't (necessarily) impractical: having 100 generic types, one for each of those fields, is what's impractical. Assuming that the question was asked with a class that contained 100 fields rather than one, hiding would be preferable to generics. Ultimately my point is that while your answer is fine for the specific question, it's not practical for larger classes. – Daniel Apr 08 '14 at 22:29
1

Implement CommunityBase as a generic, constraining the type argument to IPerson

abstract class CommunityBase<T> where T : IPerson {
{
    public abstract T Member { get; }

    public void ShowName() {
        Console.WriteLine(this.Member.FirstName + " " + this.Member.LastName);
    }
}

class University : CommunityBase<IStudent>
{
    IStudent member;

    public University(IStudent member) {
        this.member = member;
    }

    public override IStudent Member { get { return member; } }

}
Mark H
  • 13,797
  • 4
  • 31
  • 45
  • Although this answer is correct @Olivier Jacot-Descombes's answer is a bit more full. Because he removes the member field from the University class. Which was my question on the first place. – Adrian Hristov Apr 08 '14 at 21:58