참조 데이터의 값 복사
static void Main(string[] args)
{
int[] sample = { 1, 2, 3, 4, 5 };
int[] test = sample;
printIntArray(sample, "sample");
printIntArray(test, "test");
sample[1] = 777;
printIntArray(sample, "sample");
printIntArray(test, "test");
// array(int[])는 참조 형식이기 때문에 이렇게 복사한 경우 두 변수가 표기하는 값이 모두 바뀜
system("pause");
}
구조체의 값 복사
static void Main(string[] args)
{
myScore man1 = new myScore();
myScore man2;
man1.initScore(70, 24, 96);
man2 = man1;
Console.WriteLine($"man1의 평균점수 {man1._average}점");
Console.WriteLine($"man2의 평균점수 {man2._average}점");
man1.initScore(70, 24, 100);
Console.WriteLine($"man1의 평균점수 {man1._average}점");
Console.WriteLine($"man2의 평균점수 {man2._average}점");
system("pause");
}
위와 같이 찍어보면 구조체는 참조형 자료가 아닌 값 형이기 때문에 array와 달리 값 복사가 완전히 되고 있는 것이 확인된다.
구조체의 특성
구조체는 값 타입임에도 new 키워드로 생성하는데, 이것은 구조체가 값 타입 자료임에도 "치환과 대입이 가능하다"는 특성을 지니기 때문이다.
static void Main(string[] args)
{
myScore man1 = new myScore();
man1.initScore(70, 24, 96);
system("pause");
}
구조체의 생성자
구조체의 생성자는 구조체명과 동일한 이름의 메소드(class 및 struct 아래의 함수)를 만듬으로서 지정할 수 있다.
구조체는 생성자를 만들 때에 class의 생성자를 만들때와 몇가지 다른 점이 있는데
첫째. 구조체의 생성자에는 반드시 매개변수가 필요하다.
둘째. 생성자를 통해 구조체의 모든 멤버변수가 초기화되어야 한다.
public struct myScore
{
public int _kor;
public int _eng;
public int _mat;
public float _average;
//위의 모든 매개변수가 생성자에서 초기화 되어야 함
public myScore(int kor, int eng, int mat) //매개변수 필수
{
_kor = kor;
_eng = eng;
_mat = mat;
_average = (_kor + _eng + _mat) / 3f;
}
}
만들어진 생성자는 아래와 같이 사용할 수 있다.
static void Main(string[] args)
{
myScore man1 = new myScore(92, 57, 98);
system("pause");
}
구조체의 함수
아래와 같이 showInfo 함수나 init 함수를 생성할 수 있음
public struct myScore
{
public int _kor;
public int _eng;
public int _mat;
public float _average;
public myScore(int kor, int eng, int mat)
{
_kor = kor;
_eng = eng;
_mat = mat;
_average = (_kor + _eng + _mat) / 3f;
}
public void init(int kor, int eng, int mat) // 점수 입력 함수 생성자와 같은 기능
{
_kor = kor;
_eng = eng;
_mat = mat;
_average = (_kor + _eng + _mat) / 3f;
}
public void showInfo() // 점수 출력 함수
{
Console.WriteLine("==================");
Console.WriteLine($"국어 점수 : {_kor}점");
Console.WriteLine($"영어 점수 : {_eng}점");
Console.WriteLine($"수학 점수 : {_mat}점");
Console.WriteLine($"평균 점수 : {_average:F2}점");
Console.WriteLine("==================");
}
}
구조체 생성자의 오버라이딩
public struct studentInfo
{
public string _name;
score _midterm;
score _finalExam;
float _totalAverage;
public studentInfo(string name, int mk, int me, int mm, int fk, int fe, int fm)
{
_name = name;
_midterm = new score(mk, me, mm);
_finalExam = new score(fk, fe, fm);
_totalAverage = (_midterm._average + _finalExam._average) / 2;
}
public studentInfo(string name, score midterm, score finalExam)
{
_name = name;
_midterm = midterm;
_finalExam = finalExam;
_totalAverage = (_midterm._average + _finalExam._average) / 2;
}
public void showInfo()
{
Console.WriteLine($"<[{_name}]님의 성적 정보>");
Console.WriteLine("====== 중간 고사 ======");
_midterm.showInfo();
Console.WriteLine("====== 기말 고사 ======");
_finalExam.showInfo();
Console.WriteLine("====== 학기 평균 ======");
Console.WriteLine($"{_totalAverage}점");
Console.WriteLine("=======================");
}
}
오버라이딩을 통해 studentInfo 구조체 생성시, 모든 점수를 넣어도 되고, score 형태로 넣어도 되는 형태를 구현했다.
구조체 사용부
static void Main(string[] args)
{
score score1 = new score(92, 57, 98);
score score2 = new score(78, 81, 70);
studentInfo boy1 = new studentInfo("홍길동", score1, score2);
boy1.showInfo();
studentInfo boy2 = new studentInfo("김철수", 92, 70, 80, 50, 60, 70);
boy2.showInfo();
system("pause");
}
[과제1]
국어, 영어, 수학 점수와 각 점수별 등수를 저장하는 구조체를 만들고 구조체 안에 이를 출력하는 함수를 만들어 화면에 출력하세요.
(단 enum subjectType { 국어=0, 영어, 수학 } 과 동일한 형태의 enum으로 과목을 구분해야 합니다.)
[과제2]
학점자료 구조체를 만드시오. 구조체에는 학생의 이름, 평균점, 전체 등수를 가져야합니다. 추가로 [과제1]에서 만든 구조체 4개(1학기 중간고사와 기말고사, 2학기 중간고사와 기말고사)를 멤버변수로 가져야 합니다. (구조체 대신 Class 사용해도 됨)
출력화면
몇 명의 정보를 저장하시겠습니까? :
1번째 학생의 이름을 입력하세요. :
...
n번째 학생의 이름을 입력하세요. :
(각 학생의 이름은 중복되어서는 안됩니다)
(이름 입력 후 해당 학생의 각 과목별 점수가 30~100 사이로 저장되어야 합니다)
(전체 학생의 평균 점수를 계산해 저장합니다.)
(모든 학생의 이름 입력이 완료되면 각 과목의 등수와 평균점에 따른 등수를 계산합니다.)
내 답안
너무 길어져서 별도 첨부
Class와 Class 중첩
클래스는 기본적으로 복합 데이터 형식이다. 문법과 사용처가 구조체와 흡사한 부분이 많다
클래스는 객체와는 다르다, 클래스를 만든다는 것은 하나의 개념을 만드는 것과 비슷하다.
클래스의 기초문법
한정자 class 클래스명
{
필드들; //혹은 멤버변수
메소드들(){ ... }; //혹은 함수
}
클래스의 4대 속성
1. 은닉화 : 외부로부터 원치 않는 데이터의 변경을 차단한다. private로 선언된 메소드, 변수등이 대표적이다
2. 캡슐화 : 관련있는 자료와 기능을 한 번에 묶어서 제공한다. 사용자가 직접 접근을 못하더라도 정해진 부분을 통하여 사용할 수 있다.
3. 상속성 : 공통된 요소를 가진 다른 클래스에게 공통된 부분을 상속해 넘겨줄 수 있다. 즉, 재사용이 용이해져 기능의 확장과 세분화가 가능하다.
4. 다형성 : 각기 다른 객체가 동일한 기능을 각기 다른 결과를 만들게 한다. 코드의 유연성을 증대시킨다.
아래와 같이 class를 만들 수 있다. 만든 클래스는 되도록 파일을 분리해주는게 좋다.
public class People
{
//필드(멤버변수)들
string Name;
int Age;
float Height;
float Weight;
bool IsMan;
public void Init(string name, int age, float h, float w, bool isMan)
{
Name = name;
Age = age;
Height = h;
Weight = w;
IsMan = isMan;
}
public void ShowGait()
{
float bmi = Weight / (Height * Height / 10000);
Console.WriteLine($"{Name}님이 ");
if(bmi < 18)
{
Console.WriteLine("사뿐사뿐 ");
}
else if (bmi < 25)
{
if (IsMan)
{
Console.WriteLine("터벅터벅 ");
}
else
{
Console.WriteLine("총총총 ");
}
}
else
{
Console.WriteLine("쿵쿵쿵 ");
}
Console.WriteLine("걷습니다.");
}
}
using System;
using System.Collections.Generic;
namespace LearnCS_Structure2
{
/// <summary>
/// 과목별 코드
/// </summary>
public enum SubjectType
{
kor = 0,
eng,
mat,
MAX
}
/// <summary>
/// 시험별 코드
/// </summary>
public enum ExamType
{
mid1 = 0,
final1,
mid2,
final2,
MAX
}
// List와 람다식은 아직 안배웠지만..
public class Score
{
public static Random RD;
public int Number;
public SubjectType Subject;
public ExamType Exam;
public const int Min = 30, Max = 100;
public Score(ExamType exam, SubjectType subject)
{
Subject = subject;
Exam = exam;
RD = Program.RD;
Number = RD.Next(Min, Max);
}
}
public class Student
{
public string Name;
public List<Score> Scores = new List<Score>();
public float TotalAverage = 0;
public float[] ExamAverage = new float[(int)ExamType.MAX];
public float[] SubjectAverage = new float[(int)SubjectType.MAX];
public Student(string name)
{
Name = name;
for (int examCode = 0; examCode < (int)ExamType.MAX; examCode++)
{
for (int subjectCode = 0; subjectCode < (int)SubjectType.MAX; subjectCode++)
{
Scores.Add(new Score((ExamType)examCode, (SubjectType)subjectCode));
}
}
Scores.ForEach(score => TotalAverage += score.Number);
TotalAverage /= Scores.Count;
for (int examCode = 0; examCode < (int)ExamType.MAX; examCode++)
{
ExamAverage[examCode] = 0;
List<Score> tmpScores = Scores.FindAll(score => score.Exam == (ExamType)examCode);
tmpScores.ForEach(score => ExamAverage[examCode] += score.Number);
ExamAverage[examCode] /= tmpScores.Count;
}
for (int subjectCode = 0; subjectCode < (int)SubjectType.MAX; subjectCode++)
{
SubjectAverage[subjectCode] = 0;
List<Score> tmpScores = Scores.FindAll(score => score.Subject == (SubjectType)subjectCode);
tmpScores.ForEach(score => SubjectAverage[subjectCode] += score.Number);
SubjectAverage[subjectCode] /= tmpScores.Count;
}
}
}
internal class Program
{
public static Random RD = new Random();
public static List<Student> Students = new List<Student>();
static void Main()
{
// 이름 입력받기
readInt("학생 수를 입력하세요.", out int studentCount);
for (int i = 0; i < studentCount; i++)
{
string name;
while (true)
{
readString((i + 1).ToString() + "번 학생의 이름을 입력하세요.", out name);
name = name.Trim();
if (Students.Exists(student => student.Name == name) == false)
{
break;
}
Console.WriteLine($"{name} 학생은 이미 입력했습니다. 다른 학생의 이름을 입력하세요.");
}
Students.Add(new Student(name));
}
//전체 평균 정렬 및 출력
PrintRank("전체 시험 평균 순위", Students);
// 국어 전체 평균 정렬
PrintRank("전체 국어 시험 평균 순위", Students, ExamType.MAX, SubjectType.kor);
// 영어 전체 평균 정렬
PrintRank("전체 영어 시험 평균 순위", Students, ExamType.MAX, SubjectType.eng);
// 수학 전체 평균 정렬
PrintRank("전체 수학 시험 평균 순위", Students, ExamType.MAX, SubjectType.mat);
//국어
PrintRank("1학기 중간고사 국어 순위", Students, ExamType.mid1, SubjectType.kor);
PrintRank("1학기 기말고사 국어 순위", Students, ExamType.final1, SubjectType.kor);
PrintRank("2학기 중간고사 국어 순위", Students, ExamType.mid2, SubjectType.kor);
PrintRank("2학기 기말고사 국어 순위", Students, ExamType.final1, SubjectType.kor);
//영어
PrintRank("1학기 중간고사 영어 순위", Students, ExamType.mid1, SubjectType.eng);
PrintRank("1학기 기말고사 영어 순위", Students, ExamType.final1, SubjectType.eng);
PrintRank("2학기 중간고사 영어 순위", Students, ExamType.mid2, SubjectType.eng);
PrintRank("2학기 기말고사 영어 순위", Students, ExamType.final1, SubjectType.eng);
//수학
PrintRank("1학기 중간고사 수학 순위", Students, ExamType.mid1, SubjectType.mat);
PrintRank("1학기 기말고사 수학 순위", Students, ExamType.final1, SubjectType.mat);
PrintRank("2학기 중간고사 수학 순위", Students, ExamType.mid2, SubjectType.mat);
PrintRank("2학기 기말고사 수학 순위", Students, ExamType.final1, SubjectType.mat);
system("pause");
}
public static void Rank(ref List<Student> students, ExamType examType = ExamType.MAX, SubjectType subjectType = SubjectType.MAX)
{
// 전체 평균으로 순위 정렬
if (examType == ExamType.MAX && subjectType == SubjectType.MAX)
{
students.Sort((student1, student2) => (student1.TotalAverage > student2.TotalAverage) ? -1 : 1);
return;
}
// 특정 시험 + 과목의 점수로 정렬
if (examType != ExamType.MAX && subjectType != SubjectType.MAX)
{
students.Sort((student1, student2) => (
(
student1.Scores.Find(student => student.Subject == subjectType && student.Exam == examType).Number
>
student2.Scores.Find(student => student.Subject == subjectType && student.Exam == examType).Number
)
? -1 : 1
)
);
return;
}
// 특정 시험의 평균으로 정렬
if (examType != ExamType.MAX)
{
students.Sort((student1, student2) => (student1.ExamAverage[(int)examType] > student2.ExamAverage[(int)examType]) ? -1 : 1);
return;
}
// 특정 과목의 평균으로 정렬
if (subjectType != SubjectType.MAX)
{
students.Sort((student1, student2) => (student1.SubjectAverage[(int)subjectType] > student2.SubjectAverage[(int)subjectType]) ? -1 : 1);
return;
}
}
public static void PrintRank(string rankTitle, List<Student> students, ExamType exam = ExamType.MAX, SubjectType subject = SubjectType.MAX)
{
Console.WriteLine();
Console.WriteLine($"====== {rankTitle} ======");
int cnt = 1;
float printScore = 0;
Rank(ref students, exam, subject);
if (exam == ExamType.MAX || subject == SubjectType.MAX)
{
if (exam == ExamType.MAX && subject == SubjectType.MAX)
{
foreach (Student student in students)
{
printScore = student.TotalAverage;
Console.Write($"{cnt++}위 : ");
Console.Write($"{student.Name}, ");
Console.Write($"평균 {printScore:F2}점");
Console.WriteLine();
}
}
else if (exam != ExamType.MAX)
{
foreach (Student student in students)
{
printScore = student.ExamAverage[(int)exam];
Console.Write($"{cnt++}위 : ");
Console.Write($"{student.Name}, ");
Console.Write($"평균 {printScore:F2}점");
Console.WriteLine();
}
}
else // subject != SubjectType.MAX
{
foreach (Student student in students)
{
printScore = student.SubjectAverage[(int)subject];
Console.Write($"{cnt++}위 : ");
Console.Write($"{student.Name}, ");
Console.Write($"평균 {printScore:F2}점");
Console.WriteLine();
}
}
}
else
{
foreach (Student student in students)
{
printScore = student.Scores.Find(score => score.Exam == exam && score.Subject == subject).Number;
Console.Write($"{cnt++}위 : ");
Console.Write($"{student.Name}, ");
Console.Write($" {printScore:F0}점");
Console.WriteLine();
}
}
Console.WriteLine();
}
public static void readInt(string text, out int input)
{
string readLine;
int result;
while (true)
{
Console.Write(text + " : ");
readLine = Console.ReadLine();
if (int.TryParse(readLine, out result))
{
//Console.WriteLine("숫자를 입력하셨습니다. 계속 진행합니다.");
break;
}
else
{
Console.WriteLine("숫자가 아닙니다. 숫자를 입력해주세요.");
}
}
input = result;
}
public static void readString(string text, out string input)
{
Console.Write(text + " : ");
input = Console.ReadLine();
}
public static void system(string commend)
{
if (commend.CompareTo("pause") == 0)
{
Console.Write("종료하시려면 아무키나 누르세요...");
Console.ReadKey();
}
else
{
Console.WriteLine("존재하지 않는 명령어입니다...");
System.Threading.Thread.Sleep(5000);
}
}
}
}
클래스 부분은 좀 대충씀.. 디스코드에 올라오는 소스 보는게 나을 것
7/31 NCS 단위평가 공지
이런 유용한 정보를 나눠주셔서 감사합니다.