在一个方法中将抽象类作为参数传递

3

我最近开始学习C#,遇到了一个问题。我对抽象/接口类还不太熟悉,但理解了基本原理和应用。

我目前正在学习C#的面向对象编程,我已经知道面向对象编程的机制,因为我在Java中已经学过了,但是在那里我从来没有使用过抽象或接口类。

我的代码旨在传入一个对象数组列表(都是共同父类的子类),并仅打印出该特定类别的对象。这个方法可以实现,但我很好奇是否可以让相同的方法打印出所有父类的子类,如果父类是抽象的话。

父类:

abstract class Person
{
    protected string Name { get; set; }
    protected int Age { get; set; }

    public override string ToString() { return "Person:" + Name + ", " + this.Age; }
}

子类

class Student : Person
{
    private int studID { get; set; }
    private string school { get; set; }
    public Student() { }
    public Student(string name, int age, int studID, string school)
    {
        this.Name = name;
        this.Age = age;
        this.studID = studID;
        this.school = school;
    }

    public override string ToString()
    {
        string s = "Student:" + Name + ", " + Age + ", " + studID + ", " + school;
        return s;
    }
}

方法调用
    private static void StudentDetails(object type)
    {
        ArrayList tmp = new ArrayList();
            //display all
            //foreach (Person p in people) tmp.Add(p);
            foreach (Person p in people)
            {
                if (type.GetType() == p.GetType())
                {
                    tmp.Add(p);
                }
            }
            //etc...

1
你会在编译时知道类型吗? - Adam Houldsworth
6个回答

1

如果您在编译时知道类型,可以使用以下方法:

private static void StudentDetails<T>()
{
    ArrayList tmp = new ArrayList();
    foreach (Person p in people)
    {
        if (p is T)
        {
            tmp.Add(p);
        }
    }            
}

并调用

StudentDetails<Student>();

一些想法:
  1. 你应该将 ArrayList 更改为更强类型的东西,比如 List<T>。在你的情况下是 List<Person>

  2. StudentDetails 一个更好的命名。方法名应该是一个动词。


1

如果您在编译时知道类型,可以这样做:

var students = people.OfType<Student>().ToList();

完全摆脱StudentDetails方法。 OfType<T>文档可以在这里找到。

我不确定,但这也可能获取从T派生的类型(在我的示例中,派生自Student)。如果您需要List<T>而不是IEnumerable<T>,则只需要调用ToList()

如果Type参数在运行时确定,则此解决方案不会起作用,因为泛型要求编译时解析类型才能像这样编码。


1

谢谢,这正是我在寻找的。当我使用nawfal推荐的更新的StudentDetails(Type type)方法实施时,它完美地运行。 - hamhut1066
你应该始终包含来自外部链接的相关代码,以便人们在内容被删除后仍然可以查看它。 - Stan Shaw

0
你可以尝试做一些类似于以下的事情:
在这种情况下,不要使用 ArrayList,而是使用 IEnumerable,因为所有对象都是从 Person 抽象类派生而来。
private static void StudentDetails(Type filter)
{
   var filteredTypes = people.Where(p=>p.GetType() == filter);               
    ....
}

0

你可以很好地使用你的基类(即使它是抽象的)来完成这个任务:

class Program
{
    static void Main(string[] args)
    {
        List<Person> persons = new List<Person>();

        persons.Add(new Student("John", 18, 1, "Stanford"));
        persons.Add(new Student("Matt", 20, 2, "Stanford"));
        persons.Add(new Teacher("Alice", 30));

        StudentDetails(persons);
    }

    private static void StudentDetails(List<Person> people)
    {
        foreach (Person p in people)
        {
            try
            {
                Student s = (Student)p;
                Console.WriteLine(s.ToString());
            }
            catch(InvalidCastException e)
            {
            }
        }
    }
}

-1

首先:

接口和抽象类的主要区别在于你可以在基类中编写方法来为你的代码提供默认行为。众所周知,在接口中不可能做到这一点,因为你只能提供方法签名。

你提供的示例有些值得怀疑,对于这样的实现,你真的需要使用简单的基类而不是抽象类。为了提供一个基类型的集合或字段,你需要使用一个可以被实例化的基类,而这是抽象类所不允许的。

现在回答你的问题:

让我们仔细想一想。你想要一个不能被实例化的基对象,但你想要提供这个类的一个集合... 你看出问题了吧。如果你真的想这样做,那么通过许多强制转换和类型检查是有办法的,但这种方式整个想法就没有意义了。更好的做法是实现一个接口,但这样你就无法提供任何默认行为!

我的建议:

对于您的示例,请使用普通类Person并让Student继承它。您仍然可以在基类中使用默认的ToString方法,并在Student类中重写它。 对于未来的代码,请考虑以下几点: 需要基类的实例(集合,模式等)-> 简单基类或接口

需要默认行为和实例 -> 简单基类

需要没有默认行为的实例 -> 接口

需要默认行为但没有实例 -> 抽象

这些是我在决定如何实现我的类时经常思考的事情。

希望对您有所帮助

Christoph


将一个集合类型化为抽象基类根本不是问题,也不需要进行任何强制转换或类型检查。 - O. R. Mapper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接