1

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

我目前正在尝试学习面向对象编程的来龙去脉。目前我有一个新对象,如下所示:

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 学生。一个决定吉姆的成绩,另一个决定老师。在这种情况下,我想使用不同的方法,以便我可以了解它是如何完成的。

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

4

7 回答 7

5

由于您使用的是 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
}

这是你要找的吗?

于 2008-12-22T00:34:07.193 回答
3

好吧,至少三种方式。

  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 字段中。

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

于 2008-12-22T00:28:54.317 回答
1

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

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

一个 2 分钟的 OO 方式的快速示例:

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 来管理 - 这里没有令人信服的理由。

于 2008-12-22T02:02:17.610 回答
1

您仍然错过了关键点:一个将数据与其方法相关联。 因此,设置 Jim 的老师的方法是 Student 类的一部分,设置 Jim 的成绩的方法也是如此。

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

所以我们有一个班级学生。学生,当您从它创建一个对象时,需要学生姓名和学生 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 类的一部分。为什么?因为为教师添加课程与为学生添加课程意味着不同。您将向学生添加一个方法,该方法接受课程和成绩,找到课程并以某种方式分配他的成绩。(小测验:成绩是课程的一部分还是学生的一部分?)

于 2008-12-23T20:27:58.797 回答
0

好吧,这里有一个基本的误解。 您的方法与类相关联。 您所拥有的基本上是带有一些使用它们的功能的结构;没错,但根本不是 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

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

于 2008-12-22T00:37:07.250 回答
0

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

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

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

//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());
    }
}

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

于 2008-12-22T02:04:35.467 回答
0

尝试将类视为制造机器的蓝图。对象是一台机器。当你使用它时,你并不关心它在内部是如何工作的。您只需使用外部的各种控制杆和开关来控制它,以及告诉您答案的 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);
    }
}

如果您遵循这种方法,那么您将走上面向对象的道路。你还有很多有趣的东西要学——异常安全、滥用继承、松耦合……

于 2008-12-22T02:06:01.470 回答