简体   繁体   中英

How to get a List of Base class objects, from x number of child classes lists?

For the sake of explanation I made an example

public class Base {
    public string Name { get; set; }
    public string Surname { get; set; }

    public Base(string name, string surname) {
        Name = name;
        Surname = surname;
    }
}

public class Student : Base {

    public int StudentID { get; set; }

    public Student(string name, string surname, int studentID) 
                          : base(name, surname) {
        Name = name;
        Surname = surname;
        StudentID = studentID;
    }

    public override string ToString() {
        return string.Format("Name is {0}, surname is {1}, ID is {2}",
                              Name, Surname, StudentID);
    }
}

public class Teacher : Base {

    public string TeachingSubject { get; set; }

    public Teacher(string name, string surname, string teachingSubject)
                          : base(name, surname) {
        Name = name;
        Surname = surname;
        TeachingSubject = teachingSubject;
    }

    public override string ToString() {
        return string.Format("Name is {0}, surname is {1}, TeachingSubject is {2}",
                              Name, Surname, TeachingSubject);
    }
}

In my case, there are 4 child classes, but 2 is enough to keep it simple and still hit the point. Keep in mind that Student and Teacher classes contain their own property, so each object from the Base list should be different, according to the derived class properties.

Somewhere in the project I need to have a Method that returns List of Base objects

List<Base> GetAllLists() {

    return allLists; //Lets name the return list of Base objects like this
}

So I need to put lists of objects from derived classes to a Base class List and use it somewhere.

I already did something like this

List<Base> GetAllLists() {

    var allLists = new List<Base>();

    var studentList = new StudentService().GetList();  //Service class that populates the list of students from a csv file
    foreach (var item in studentList) {
        allLists.Add(new Student(item.Name, item.Surname, item.StudentID));
    }
    var teacherList = new TeacherService().GetList();  //Service class that populates the list of teachers from a csv file
    foreach (var item in teacherList) {
        allLists.Add(new Teacher(item.Name, item.Surname, item.TeachingSubject));
    }

    return allLists;
}

I got a comment:

there's no need to iterate all of the lists and instantiate a new object of each entity, since all of them inherit from the same base class

So how to achieve this in some other way?

I guess the AddRange method will allow you to add all objects in one blow:

var allLists = new List<Base>();

var studentList = new StudentService().GetList();  

allLists.AddRange(studentList);

var teacherList = new TeacherService().GetList();  

allLists.AddRange(teacherList);

Since Student and Teacher inherit from base they just can fit into the base List.

The big difference here is that you add only references! that means if you change a value in the original studentList it will also be changed in the allLists list!

Whereas in your posted code this will not happen, because you create a new instance for each entry in your studentList . This way you create independence between the two lists.

But if studentList is only a local variable that is not manipulated elsewhere it is legitimate to use AddRange and add simply the references to the allLists list

Here is a small test program. Copy paste it into a console application and see the difference for yourself. Just change the copyreference value and compare the outcome:

void Main()
{       
    bool copyreference = true;

var allLists = new List<Base>();

    var studentList  = new List<Student>();   
    studentList.Add(new Student("Alf", "Bedrock", 123));
    studentList.Add(new Student("Alfine", "Bedrock", 456)); 

    var teacherList  = new List<Teacher>();    
    teacherList.Add(new Teacher("Brad", "Gulp", "MATH"));
    teacherList.Add(new Teacher("Evelyn", "Gulp", "BIO"));


    if (copyreference)
    {
        allLists.AddRange(studentList);
        allLists.AddRange(teacherList);
    }
    else
    {
        foreach (var item in studentList)
        {
            allLists.Add(new Student(item.Name, item.Surname, item.StudentID));
        }
    }

    Console.WriteLine(String.Join(Environment.NewLine, allLists));
    // TEST changing a value in the original list
    studentList[0].Name = "Harry";
    // if you copied references you will see the change in the final list
    Console.WriteLine(Environment.NewLine + String.Join(Environment.NewLine, allLists));
}

I would do the following, if it is allowed to create a new instance of a list when the GetAllLists method is called

public List<Base> GetAllLists() {
    var studentList = new StudentService().GetList();  
    var teacherList = new TeacherService().GetList();  

    return studentList.Cast<Base>()
        .Concat(teacherList.Cast<Base>())
        .ToList();
}
  1. You don't need to write Name = name; Surname = surname; Name = name; Surname = surname; since you already have : base(name, surname)

  2. Regarding to the shadow/deep copy in AddRange() , you can also implement ICloneable or add a copy constructor. Example:

Class definations:

public class Base {
    public string Name { get; set; }
    public string Surname { get; set; }

    public Base(string name, string surname) {
        Name = name;
        Surname = surname;
    }
}

public class Student : Base, ICloneable {  // ICloneable

    public int StudentID { get; set; }

    public Student(string name, string surname, int studentID) : base(name, surname) {
        //Name = name;
        //Surname = surname;
        StudentID = studentID;
    }

    public override string ToString() {
        return string.Format("Name is {0}, surname is {1}, ID is {2}", Name, Surname, StudentID);
    }

    public object Clone() {
        return MemberwiseClone();
    }
}

public class Teacher : Base {  // Copy constructor

    public string TeachingSubject { get; set; }

    public Teacher(string name, string surname, string teachingSubject) : base(name, surname) {
        //Name = name;
        //Surname = surname;
        TeachingSubject = teachingSubject;
    }

    public override string ToString() {
        return string.Format("Name is {0}, surname is {1}, TeachingSubject is {2}", Name, Surname, TeachingSubject);
    }

    public Teacher(Teacher obj) : base(obj.Name, obj.Surname) {
        TeachingSubject = obj.TeachingSubject;
    }
}

Sample usage:

// AddRange()
List<Student> stud = new List<Student>(2);
stud.Add(new Student("s1", "ss1", 1));
stud.Add(new Student("s2", "ss2", 2));
List<Teacher> teac = new List<Teacher>(2);
teac.Add(new Teacher("t1", "ts1", "subject1"));
teac.Add(new Teacher("t2", "ts2", "subject2"));

List<Base> bas = new List<Base>(4);
bas.AddRange(stud);
bas.AddRange(teac);
Debug.Print($"{Environment.NewLine}AddRange():");
Debug.Print($"Base List:{Environment.NewLine}{bas[0]}{Environment.NewLine}{bas[1]}{Environment.NewLine}{bas[2]}{Environment.NewLine}{bas[3]}");

stud[0] = new Student("s3_changed", "ss3_changed", 3);
stud[1].Name = "s4_changed";
stud[1].Surname = "ss4_changed";
stud[1].StudentID = 4;
bas[2].Name = "b3_changed";
bas[2].Surname = "bs3_changed";
((Teacher)bas[2]).TeachingSubject = "bsub3_changed";
bas[3] = new Teacher("t4_changed", "ts4_changed", "tsbu4_changed");
Debug.Print($"{Environment.NewLine}After Change:");
Debug.Print($"Student List:{Environment.NewLine}{stud[0]}{Environment.NewLine}{stud[1]}");
Debug.Print($"Teacher List:{Environment.NewLine}{teac[0]}{Environment.NewLine}{teac[1]}");
Debug.Print($"Base List:{Environment.NewLine}{bas[0]}{Environment.NewLine}{bas[1]}{Environment.NewLine}{bas[2]}{Environment.NewLine}{bas[3]}");

// Concat()
List<Student> stud2 = new List<Student>(2);
stud2.Add(new Student("s1", "ss1", 1));
stud2.Add(new Student("s2", "ss2", 2));
List<Teacher> teac2 = new List<Teacher>(2);
teac2.Add(new Teacher("t1", "ts1", "subject1"));
teac2.Add(new Teacher("t2", "ts2", "subject2"));

List<Base> bas2 = new List<Base>();
bas2 = bas2.Concat(stud2).Concat(teac2).ToList();
Debug.Print($"{Environment.NewLine}Concat:");
Debug.Print($"Base List:{Environment.NewLine}{bas2[0]}{Environment.NewLine}{bas2[1]}{Environment.NewLine}{bas2[2]}{Environment.NewLine}{bas2[3]}");

stud2[0] = new Student("s3_changed", "ss3_changed", 3);
stud2[1].Name = "s4_changed";
stud2[1].Surname = "ss4_changed";
stud2[1].StudentID = 4;
bas2[2].Name = "b3_changed";
bas2[2].Surname = "bs3_changed";
((Teacher)bas2[2]).TeachingSubject = "bsub3_changed";
bas2[3] = new Teacher("t4_changed", "ts4_changed", "tsbu4_changed");
Debug.Print($"{Environment.NewLine}After Change:");
Debug.Print($"Student List:{Environment.NewLine}{stud2[0]}{Environment.NewLine}{stud2[1]}");
Debug.Print($"Teacher List:{Environment.NewLine}{teac2[0]}{Environment.NewLine}{teac2[1]}");
Debug.Print($"Base List:{Environment.NewLine}{bas2[0]}{Environment.NewLine}{bas2[1]}{Environment.NewLine}{bas2[2]}{Environment.NewLine}{bas2[3]}");

// AddRange() clone
List<Student> stud3 = new List<Student>(2);
stud3.Add(new Student("s1", "ss1", 1));
stud3.Add(new Student("s2", "ss2", 2));
List<Teacher> teac3 = new List<Teacher>(2);
teac3.Add(new Teacher("t1", "ts1", "subject1"));
teac3.Add(new Teacher("t2", "ts2", "subject2"));

List<Base> bas3 = new List<Base>(4);
bas3.AddRange(stud3.Select(x => (Student)x.Clone()).ToList());  // Clone
bas3.AddRange(teac3.Select(x => new Teacher(x)).ToList());  // Copy c'tor
Debug.Print($"{Environment.NewLine}AddRange() clone:");
Debug.Print($"Base List:{Environment.NewLine}{bas3[0]}{Environment.NewLine}{bas3[1]}{Environment.NewLine}{bas3[2]}{Environment.NewLine}{bas3[3]}");

stud3[0] = new Student("s3_changed", "ss3_changed", 3);
stud3[1].Name = "s4_changed";
stud3[1].Surname = "ss4_changed";
stud3[1].StudentID = 4;
bas3[2].Name = "b3_changed";
bas3[2].Surname = "bs3_changed";
((Teacher)bas3[2]).TeachingSubject = "bsub3_changed";
bas3[3] = new Teacher("t4_changed", "ts4_changed", "tsbu4_changed");
Debug.Print($"{Environment.NewLine}After Change:");
Debug.Print($"Student List:{Environment.NewLine}{stud3[0]}{Environment.NewLine}{stud3[1]}");
Debug.Print($"Teacher List:{Environment.NewLine}{teac3[0]}{Environment.NewLine}{teac3[1]}");
Debug.Print($"Base List:{Environment.NewLine}{bas3[0]}{Environment.NewLine}{bas3[1]}{Environment.NewLine}{bas3[2]}{Environment.NewLine}{bas3[3]}");

Output:

AddRange():
Base List:
Name is s1, surname is ss1, ID is 1
Name is s2, surname is ss2, ID is 2
Name is t1, surname is ts1, TeachingSubject is subject1
Name is t2, surname is ts2, TeachingSubject is subject2

After Change:
Student List:
Name is s3_changed, surname is ss3_changed, ID is 3
Name is s4_changed, surname is ss4_changed, ID is 4
Teacher List:
Name is b3_changed, surname is bs3_changed, TeachingSubject is bsub3_changed
Name is t2, surname is ts2, TeachingSubject is subject2
Base List:
Name is s1, surname is ss1, ID is 1
Name is s4_changed, surname is ss4_changed, ID is 4
Name is b3_changed, surname is bs3_changed, TeachingSubject is bsub3_changed
Name is t4_changed, surname is ts4_changed, TeachingSubject is tsbu4_changed

Concat:
Base List:
Name is s1, surname is ss1, ID is 1
Name is s2, surname is ss2, ID is 2
Name is t1, surname is ts1, TeachingSubject is subject1
Name is t2, surname is ts2, TeachingSubject is subject2

After Change:
Student List:
Name is s3_changed, surname is ss3_changed, ID is 3
Name is s4_changed, surname is ss4_changed, ID is 4
Teacher List:
Name is b3_changed, surname is bs3_changed, TeachingSubject is bsub3_changed
Name is t2, surname is ts2, TeachingSubject is subject2
Base List:
Name is s1, surname is ss1, ID is 1
Name is s4_changed, surname is ss4_changed, ID is 4
Name is b3_changed, surname is bs3_changed, TeachingSubject is bsub3_changed
Name is t4_changed, surname is ts4_changed, TeachingSubject is tsbu4_changed

AddRange() clone:
Base List:
Name is s1, surname is ss1, ID is 1
Name is s2, surname is ss2, ID is 2
Name is t1, surname is ts1, TeachingSubject is subject1
Name is t2, surname is ts2, TeachingSubject is subject2

After Change:
Student List:
Name is s3_changed, surname is ss3_changed, ID is 3
Name is s4_changed, surname is ss4_changed, ID is 4
Teacher List:
Name is t1, surname is ts1, TeachingSubject is subject1
Name is t2, surname is ts2, TeachingSubject is subject2
Base List:
Name is s1, surname is ss1, ID is 1
Name is s2, surname is ss2, ID is 2
Name is b3_changed, surname is bs3_changed, TeachingSubject is bsub3_changed
Name is t4_changed, surname is ts4_changed, TeachingSubject is tsbu4_changed

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM