【问题标题】:Using Objects across methods跨方法使用对象
【发布时间】:2010-09-27 23:12:02
【问题描述】:

再次编辑: 我想我现在明白了。那么我需要做的就是使用我希望能够访问的类的当前类冒号?人:学生,或人:老师 对吗?

我目前正在尝试学习面向对象编程的来龙去脉。目前我有一个类似于以下内容的新对象:

class student
{
    int grade; //0 - 100 as opposed to the characters A, B, C, etc.
    string teacher; //For the teacher of the class the student is in.

}

class teacher
{ 
    int courseAverage;
    string name; 
    //teacher.name would correspond to the teacher in the student class.
}

class Program
{
    student Jim;
    Jim = new student();
    teacher John;
    John = new teacher();
}

static void grades()
{
    Jim.grade = 100;
}

static void teacher()
{
    Jim.teacher = "John Smith";
}

static void average()
{
    int average; //For the sake of simplicity I'll leave the average at an int.
    average = (Jim.grade + Sue.grade + Sally.grade + Robert.grade) / 4;
    /*A loop would be set before the average variable to 
    ensure that only students of John would be counted in this.*/

}

static void teacheraverage()
{
    John.courseAverage = average;//from the average method.
}

编辑:

我想做的是修改另一个类的信息。但是,我想在程序类中以一种方法修改来自 Jim 学生的信息。一种计算具有给定教师的学生的平均成绩的方法。

另外,我在这些中使用静态的唯一原因是因为这是我设法跨方法访问变量的唯一方法。我尝试使用静态方法跨类使用方法,但没有成功。有没有其他方法可以做到这一点?

我想以多种方式使用 Jim 学生。一个决定吉姆的成绩,另一个决定老师。在这种情况下,我想使用不同的方法,以便我可以了解它是如何完成的。

好的,看来我的理解不正确。我将尝试类方法中的方法。

【问题讨论】:

标签: c# methods oop


【解决方案1】:

您仍然缺少关键点:将数据与其方法相关联。因此,设置 Jim 的老师的方法是 Student 类的一部分,如是设置吉姆成绩的方法。

首先用自然语言来思考它。 “Jim 是一名学生;学生有姓名、学生 ID 号和班级。班级有教师和教室。学生在学期末收到每个班级的成绩。”

所以我们有一个班级学生。学生,当您从中创建对象时,需要学生姓名和学生 ID,因此“构造函数”需要具有名称字符串和学生 ID(也称为字符串)来初始化它。我不懂C#,所以我会用大括号制作一个伪代码......

class Student {
    String name;
    String studentID;
    Course crsList = List<Course>; // "list of courses."
    Student(String name, String studentID ){
        // Make a new student, the "constructor"
        this.name = name;
        this.studentID = studentID;
    }
    addCourse(Course c){
        // Add a course to the student's list
        this.crsList.append(c); // Adds c to end of crsList
    }
}

这表示“学生有姓名和 ID,我们可以将课程添加到他们的课程集合中。”

class Teacher {
    String name;
    Course crsList = List<Course>;
    Teacher(String name){
        this.name = name;
    }
    // ... more methods here.
}

但是我们观察到老师和学生都是两种人,所以我们“重构”了这个把共同的部分放在一起

class Person {
    String name;
    Person(String name){
        this.name = name;
    }
    // ... more methods
}

现在我们改变老师和学生,所以我们说

class Student : Person {
    String studentID;
    Student(String name, String studentID){
        // Now we need to initialize the Person part
        super(name);
        // and the special Student part
        this.studentID = studentID;
    }
}

现在说“学生是一种具有额外学生 ID 并且我们可以向其添加课程的人。”

您将继续为 Teach 定义“addClass”,但作为 Teacher 类的一部分。为什么?因为为教师添加课程与为学生添加课程意味着不同。您将向学生添加一个方法,该方法接受课程和成绩,找到课程并以某种方式分配他的成绩。 (小测验:成绩是课程的一部分还是学生的一部分?)

【讨论】:

  • 这非常清楚,对我来说仍然有点粗糙,但现在我得到了非常基本的概念。就弹出测验而言,我认为它将成为学生的一部分,因为学生将获得一门课程的成绩。课程有学生的成绩,对吗?
  • 是的。我们需要更多的用例来确切地知道如何(我们是否有要求来查看,例如,一门课程的平均成绩是多少)但我的第一个想法是学生之间会有关联和一个课程实例; ...
  • ... 您将有一个“assignGrade”方法,根据课程设置学生的成绩。 (如果您需要对课程进行制表,您可以使用关联从 Student 中找到它。)
【解决方案2】:

尝试将类视为制造机器的蓝图。对象是一台机器。当你使用它时,你并不关心它在内部是如何工作的。您只需使用外部的各种控制杆和开关来控制它,以及告诉您答案的 LED 等等。这对你来说是一个“黑匣子”。

控制杆和开关是方法和属性。它们公开了一个用于与对象交互的“用户界面”。

方法中的代码是内部的。它的工作原理是一个秘密。这使对象更易于使用(如果设计得当)。最终存储信息的字段也是秘密。类方法之外的代码不能直接访问它们。

public class Student
{
    // Notice how these details are private
    private List<Subjects> _subjects = new List<Subjects>();
    private DateTime _started;

    // A property that only has a 'getter' because the value shouldn't be modified
    public DateTime Started
    {
        get { return _started; }
    }

    // Methods carry out actions. This one calls on to a method in another object.
    public void Enroll(Subject s)
    {
        if (_subjects.Contains(s))
            throw new Exception("Already studying that course");

        s.RegisterStudent(this);
        _subjects.Add(s); 
    }

    public void Flunk(Subject s)
    {
        if (!_subjects.Contains(s))
            throw new Exception("Can't flunk that subject, not studying it");

        s.RemoveStudent(this);
        _subjects.Remove(s);
    }
}

如果您遵循这种方法,那么您就正在走向面向对象。你仍然有很多有趣的事情要学习——异常安全、滥用继承、松散耦合......

【讨论】:

    【解决方案3】:

    您的模型过于简单且未指定。但让我们暂时忽略它;-)

    在下面的示例中,我也忽略了属性的细节并存根构造函数,以免混淆主要的建模问题

    学生和老师都是人(正如查理·马丁和其他人指出的那样)

    //people have names
    public class Person
    {
        public Person(string _name)
        {
            Name = _name;
        }
        public string Name;
    }
    

    学生是一个人,有一个年级和一个老师

    //students are people with a grade and a teacher
    public class Student : Person
    {
        public Student(string _name, int _grade, 
            Teacher _teacher) : base(_name)
        {
            Grade = _grade;
            Teacher = _teacher;
            Teacher.Students.Add(this);  //add to the teacher's collection
        }
        public int Grade;
        public Teacher Teacher;
    }
    

    教师是人,具有课程平均成绩和一组学生。

    public class Teacher : Person
    {
        public Teacher(string _name) : base(_name)
        {
        }
        public int CourseAverage;
        public IList<Student> Students = new List<Student>();
        public void ComputeAverageGrade()
        {
            int sum = 0;
            foreach(Student s in Students)
            {
                sum += s.Grade;
            }
            CourseAverage = sum / Students.Count;
        }
    }
    

    在您的示例中,您有一些学生并计算平均成绩

    public class Program
    {
        public static void Main(string [] args)
        {
            Teacher JohnSmith = new Teacher("John Smith");
    
            Student Jim = new Student("Jim",100,JohnSmith);
            Student Sue = new Student("Sue",90,JohnSmith);
            Student Sally = new Student("Sally",70,JohnSmith);
            Student Robert = new Student("Robert",100,JohnSmith);
    
            //add our resident overachiever
            Student JonSkeet = new Student("Jon Skeet",150,JohnSmith);
    
            JohnSmith.ComputeAverageGrade();
    
            Console.WriteLine("Course Average for " + JohnSmith.Name + " is " +
                JohnSmith.CourseAverage.ToString());
        }
    }
    

    在更现实的模型中,每个学生可能会学习不止一门课程,而每位教师可能会教不止一门课程,因此您需要对课程进行建模。课程仅在一个学期或一个季度等期间存在。

    【讨论】:

      【解决方案4】:

      lassevk 很好地回答您的问题 - 基本上,您如何通过多种方法访问名为 Jim 的 Student 实例。

      不过,我想指出的是,您正在这里编写程序代码。并不是说这一定有什么问题 - 只是你在这里并没有真正“得到”OO。

      一个 2 分钟的面向对象方式的快速示例:

      class Student {
         public string Name { get; private set; }
      
         public Student(string name) {
            if (string.IsNullOrEmpty(name)) throw new ArgumentException();
            this.Name = name;
         }
      }
      
      class Teacher {
         public string Name { get; private set; }
      
         public List<Course> Courses { get; private set; }
      
         public Grade GetAverageCourseGrade() {
             int totalCourseGrade;
             foreach (Course course in this.Courses) {
                totalCourseGrade += course.GetAverageGrade().Value;
             }
             return new Grade(totalCourseGrade / this.Courses.Count);
         }
      
         public Teacher(string name) {
             if (string.IsNullOrEmpty(name)) throw new ArgumentException();
             this.Name = name;
             this.Courses = new List<Course>();
         }
      }
      
      class Course {
         public string Name { get; private set; }
      
         public IEnumerable<Student, Grade> StudentGrades { get; private set; }
      
         public Grade GetAverageGrade() {
            int totalGrade;
            // could use LINQ, but I'll make it slightly more explicit
            foreach (KeyValuePair<Student, Grade> studentGrade in this.StudentGrades) {
               Grade grade = kvp.Value;
               totalGrade += grade.Value;
            }
            return new Grade(totalGrade / this.StudentGrades.Count());
         }
      
         public Course(string name) {
             if (string.IsNullOrEmpty(name)) throw new ArgumentException();
             this.Name = name;
             this.StudentGrades = new Dictionary<Student, Grade>();
         }
      }
      
      class Grade {
         public int Value { get; private set; }
      
         public char Letter { 
             get {
                switch (this.Value / 10) {
                   case 10: case 9:
                     return 'A';
                   case 8:
                     return 'B';
                   case 7:
                     return 'C';
                   case 6:
                     return 'D';
                   default:
                     return 'F';
                }
             }
         }
      
         public Grade(int value) {
            if (value < 0 || value > 100) throw new ArgumentOutOfRangeException();
            this.Value = value;
         }
      }
      
      class Program {
         static int Main(string[] args) {
            Teacher john = new Teacher("John Smith");
      
            Course cs101 = new Course("Computer Science 101");
            john.Courses.Add(cs101);
      
            Student mark = new Student("Mark Brackett");
            Student xesaniel = new Student("Xesaniel");
      
            cs101.Students.Add(mark, new Grade(90));
            cs101.Students.Add(xesaniel, new Grade(95));
      
            Console.WriteLine(john.GetAverageCourseGrade());
         }
      }
      

      那里有很多仪式和噪音,但重要的是教师和课程知道如何计算自己的平均成绩并验证/执行自己的内部状态。 Main 方法实际上只负责协调设置和类之间的任何交互。

      从技术上讲,这里的很多关系都是双向的——例如,一个老师有很多课程,而一个课程只有一个老师。但是,双向关系可以是一个 PITA 来管理 - 这里没有令人信服的理由。

      【讨论】:

        【解决方案5】:

        嗯,至少三种方式。

        1. 给Student班级打分和老师方法:

        public class Student
        {
            int grade; //0 - 100 as opposed to the characters A, B, C, etc.
            string teacher; //For the teacher of the class the student is in.
        
            public void SetGrades()
            {
                grade = 100;
            }
        
            public void SetTeacher()
            {
                teacher = "John Smith";
            }
        }
        
        class Program
        {
            public static void Main()
            {
                Student Jim;
                Jim = new Student();
                Jim.SetTeacher();
                Jim.SetGrades();
            }
        }
        

        或 2. 将学生传递给方法:


        class Program
        {
            public static void Main()
            {
                Student Jim;
                Jim = new Student();
                grades(Jim);
                teacher(Jim);
            }
        
            static void grades(Student student)
            {
                student.grade = 100;
            }
        
            static void teacher(Student student)
            {
                student.teacher = "John Smith";
            }
        }
        

        或 3. 在 Program 类中使用 Jim 作为变量,因此您实际上不必将它传递给任何东西


        class Program
        {
            static Student Jim;
        
            public static void Main()
            {
                Jim = new Student();
                grades();
                teacher();
            }
        
            static void grades()
            {
                Jim.grade = 100;
            }
        
            static void teacher()
            {
                Jim.teacher = "John Smith";
            }
        }
        

        请注意,在所有示例中,我让 Student 类的名称中都有一个大写的 S,以将其与参数和您可能拥有的任何局部变量区分开来。

        了解这些差异很重要。

        在第一个解决方案中,您要求对象本身设置成绩和老师。如果这些是你经常做的操作,它们可能属于类本身,而不是外部的操作。

        在第二个解决方案中,您绕过学生。这使您可以轻松地与多个学生一起工作,因为代码并未硬连线以与单个对象一起工作。

        在第三种解决方案中,您无需传递任何内容,但您的代码现在被锁定为在 Jim 字段中一次只能与一名学生合作。

        就我个人而言,我会制定第一个解决方案的变体,只是在设置的等级和教师方面更灵活一些。可能使用属性(您还应该更多地研究。)

        【讨论】:

          【解决方案6】:

          好的,这里有一个基本的误解。 您的方法与类相关联。您所拥有的基本上是结构,其中包含一些使用它们的函数;没错,但根本不是OO。

          你真正想要的是这样的(希望是好的说明性伪代码):

          class Person
            name : String
            Person(namearg: String) -- your constructor
              name = namearg;
            end
          end
          
          class Student :: Person -- read :: as "is a kind of"
            Student(namearg : String)
              super(namearg) -- calls the Person ctor
            end
            -- more stuff will go in here
          end
          -- Teacher looks much the same
          
          class Course
            courseName : String
            teacher : Teacher
            students : list of Student
            Course(namearg : string)
              courseName = namearg 
            end
            addStudent(s : student)
              students.append(s) -- add student to the end of the list
            end
            -- and so on.
          end
          

          关键是一个类知道关于它所描述的事物的所有信息:它使用的数据和可以执行的操作。

          【讨论】:

          • 问题是使用 C# 示例提出的,因为使用 VB 语法的 .net 问题的初学者级别会令人困惑且没有帮助。
          • 我一直在努力解决这个问题。谁能稍微解释一下?我可能只需要一点点推动就可以让我继续前进。
          • 查理是对的,但他选择向提问者提出一个新问题(学习解析动态发明的类似 Ruby 的语法)可能没有帮助。
          • 我同意这是正确的答案,如果具有所需权力的人编辑答案以使用 C#。然后就可以投票了。
          • 语法其实更像Pascal;如果有人想给我发 C#,我会输入它。我不懂 C#。
          【解决方案7】:

          由于您使用的是 C#,并且假设您使用的是 .NET 3.5,您的类可能看起来像这样:

          public class Student
          {
              public int Grade { get; set; }
              public string Teacher { get; set; }
          }
          

          Grade 和 Teacher 是自动属性,大致相当于 Java 中的 getter 和 setter,也就是方法。 C# 中的属性本质上是获取和设置类的本地成员的快捷方式。

          您对上述类的用法如下所示:

          public class Program
          {
              // creates a new Student object with the properties Grade and Teacher set.
              Student s = new Student() { Grade = 100, Teacher = "John Smith" }
          
              Console.Write(s.Grade); // outputs 100
              Console.Write(s.Teacher); // outputs John Smith
          }
          

          这就是你要找的吗?

          【讨论】:

          • 不完全是。我想做的是修改另一个类的信息。但是,我想在程序类中以一种方法修改来自 Jim 学生的信息。一种计算拥有给定老师的学生平均成绩的方法。
          • 您可以像变量一样使用属性来修改信息、年级和教师。 s.Grade = 50;, s.Teacher = "Jane Doe";
          猜你喜欢
          • 1970-01-01
          • 2021-06-07
          • 1970-01-01
          • 1970-01-01
          • 2010-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-18
          相关资源
          最近更新 更多