using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// MapEditor.cs에선 List<NoteBlockData>만 사용, MapSaveLoad.cs에서 파일을 저장하고 불러옴
/// </summary>
public class MapSaveLoad
{
// test
public void saveMapData(List<NoteBlockData> noteBlockDataList)
{
// TODO: noteBlockDataList에 있는 데이터를 easy save로 저장
}
public List<NoteBlockData> loadMapData(string mapName)
{
List<NoteBlockData> noteBlockDataList = new List<NoteBlockData>();
// TODO: easy save로 저장된 데이터를 불러와서 noteBlockDataList에 저장
return noteBlockDataList;
}
}
일단 여기까지 뼈대만 만들어놓고 테스트해봤는데

통곡의 벽에 가로막혔다.
static해봐도 안되고 뭘 어떻게 해야되는거지
내일 해결해야할듯.
다른 파일에 클래스 정의해놓고 쓰는게 이렇게 어려우면 객체 지향 도대체 어떻게 하는거지
property란
클래스의 필드에 접근하거나 수정하는 방식을 정의하는 메서드이다.
쉽게 비유하면,
변수를 직접 건드리는 건 막아놓고, 문지기를 하나 둬서
해당 변수와의 상호작용은 전부 문지기를 통하게 하는 것이다.
장점
변수에 대한 제어를 통제할 수 있다.
누가 변수를 사용했는지, 어떻게 수정했는지 알 수 있다.
값을 수정하기 전에 유효성 검사를 할 수 있다.
public class Example
{
public int MyProperty { get; set; } // 자동 구현 프로퍼티
}
이렇게 간략하게 써놓으면
private int _myProperty; // 자동 생성된 백킹 필드
public int MyProperty
{
get { return _myProperty; }
set { _myProperty = value; }
}
컴파일러가 알아서 property답게 만들어준다.
하지만 이래서야 그냥 필드를 선언한 것과 차이점이 없어보인다.
public int MyProperty; 이것과 완벽하게 하는 일이 똑같지 않은가?
굳이 필드 대신 자동 구현 속성을 사용하는 이유가 몇 가지 있다.
Data binding이란?
앱 UI와 해당 UI가 표시하는 데이터를 연결하는 프로세스이다.
데이터 값이 변경될 때 데이터에 바인딩된 요소에 변경 사항이 자동으로 반영된다.
읽으면 좋을만한 글
https://csharpindepth.com/Articles/PropertiesMatter
속성(property)은 필드 값을 읽거나, 쓰거나, 계산하는 방법을 제공하는 클래스 속성을 나타내는 멤버
프로퍼티: 괄호가 없는 메서드. private 성격이 있는 필드를 public 속성으로 외부에 공개할 때 사용.
public class Car
{
private string name;
public string Name { get { return name; } set { name = value; } }
}
Car c1 = new Car();
c1.Name = "Car1";
Console.WriteLine(c1.Name);
class Car
{
public string Name { get; set; }
}
이렇게 한 줄로 프로퍼티를 정의하는 걸 auto property라고 함.
public static string Name { get; set; } = "길벗";
속성 선언과 동시에 기본값으로 초기화 가능
public string Message { get; private set; } = "읽기 전용 속성";
set에 private을 사용하면 수정이 불가능한 읽기 전용 속성을 만들 수 있다.
public int Count
{
get => count;
set => count = value;
}
> class Course
. {
. public int Id { get; set; }
. public string Title { get; set; }
. }
> // 1. Initialize with property
> Course csharp = new Course(); csharp.Id = 1; csharp.Title = "C#";
> Console.WriteLine($"{csharp.Id} - {csharp.Title}");
1 - C#
> // 2. Inittialize with object initializer
> Course aspnet = new Course() { Id = 2, Title = "ASP.NET" };
> Console.WriteLine($"{aspnet.Id} - {aspnet.Title}");
2 - ASP.NET
개체 이니셜라이저를 이용하면 좀 더 쉽게 속성을 초기화할 수 있다.
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name; Age = age;
}
(...)
Person PC = new Person("백승수", 21);
개체를 생성할 때 생성자로도 속성을 초기화할 수 있다.
class Car
{
public string Maker { get; set; }
}
> Car car = new Car();
> Console.WriteLine(nameof(car.Maker));
Maker
nameof 연산자를 사용하면 속성이나 메서드 이름을 문자열로 가져올 수 있다.
> var person = new { Name = "백승수", Age = 21 };
> person.Name
"백승수"
> person.Age
21
익명 형식을 사용하면 특정 클래스로 형식을 만들 필요 없이 간단히 객체를 생성해낼 수 있다.
"새인데 오리처럼 생겼고, 오리처럼 수영하며, 오리처럼 꽥꽥대면 나는 그 새를 오리라고 하겠다."
익명 형식으로 개체 만든 후에
속성과 형식이 다른 개체를 대입하면 오류 난다.
처음 개체가 만들어지면 그 형식과 동일한 형태로만 다시 할당되는 것을 덕 타이핑이라 한다.

덕 타이핑이란, “내가 정의한 동작을 할 수 있다면 그 타입으로 인정해주는 것이다.” 어렵게 말하면 “객체의 변수, 메소드의 집합이 객체의 타입을 결정하는 것”을 말한다.
사진을 보자. 당장 저것의 이름이 콘센트인지 돼지코인지는 중요하지 않다. 내가 정의한 동작(코드를 꽂을 수 있다!)를 실행할 수 있다면 콘센트로 간주할 수 있다는 것이다.
프로그램을 작성하다보면 특정 속성은 반드시 특정 값으로 초기화해야 할 때가 있다.
즉, null 또는 빈 값이 들어오면 안 될 때가 있는데, 이때는 생성자를 사용하여 반드시 특정 값으로 넘겨주도록 강제할 수 있다.
class Car
{
public string Name { get; private set; }
public Car(string name)
{
if (string.IsNullOrEmpty(name))
{
// 빈 값이면 강제로 ArgumentException 예외 발생
throw ne ArgumentException();
}
this.Name = name;
}
}
class Fish
{
public int Weight { get; set; } = 50;
public void Feed(int weight) => Weight += weight;
class PropertyPractice
{
static void Main()
{
var fish = new Fish();
fish.Weight = 10;
fish.Feed(15);
Console.WriteLine(fish.Weight);
}
}
}
class Person
{
public string Name { get; set; }
public Address Address { get; set; }
}
class Adress
{
public string Street { get; set; } = "알 수 없음";
}
(...)
WriteLine($"person?.Name ?? "아무개"}은(는)" + $"{person?.Address.Street ?? "아무곳"}에 삽니다.");
WriteLine($"첫 번째 사람 : {otherPeople?[0]?.Name ?? "없음"}");
Name 속성이 null이 아니면 해당 Name 속성 사용, 아니면 null 반환하여 ?? 연산자 통해 "아무개" 반환
otherPeople?[0]?.Name은 [0]번째 인덱스의 배열 값이 null인지 확인
public string this[int index]
{
get { return (index % 2 == 0) ? "짝수" : "홀수"; }
}
인덱서는 클래스의 인스턴스를 배열처럼 사용할 수 있게 하는 구문이다.
쉽게 말해서, 인스턴스 이름 옆에 [index] 붙이면 무슨 행동을 할지 정의하는 메서드이다.
아래의 코드를 보면 조금 더 이해가 잘 된다.
public class Person
{
public string Name { get; set; }
public string Gender { get; set; }
public int Age { get; set; }
public string Email { get; set; }
public Object this [int index]
{
get
{
if (index == 0)
return Name;
else if (index == 1)
return Gender;
else if (index == 2)
return Age;
else if (index == 3)
return Email;
else
return null;
}
set
{
if (index == 0)
Name = value.ToString();
else if (index == 1)
Gender = value.ToString();
else if (index == 2)
Age = Convert.ToInt32(value);
else if (index == 3)
Email = value.ToString();
}
}
public override string ToString()
{
return "Name: " + Name + " / Gender: " + Gender + " / Age: " + Age + " / Email: " + Email;
}
}
class Program
{
static void Main(string[] args)
{
Person person = new Person() {
Name="Tom",
Gender="Male",
Age= 20,
Email="Tom@Test.com"
};
Console.WriteLine("Name: " + person[0]);
Console.WriteLine("Gender: " + person[1]);
Console.WriteLine("Age: " + person[2]);
Console.WriteLine("Email: " + person[3]);
}
}
코드 출처: https://developer-talk.tistory.com/323
person[0] 넣으면 Name를 반환하고,
person[1] 넣으면 Gender를 반환한다.
이렇게 멤버에 인덱싱을 해줄 수 있는게 바로 인덱서다.
숫자 말고 문자열로 인덱싱을 해줄 수도 있다.
public class Car
{
private string[] names;
public Car(int length) { names = new string[length]; }
public string this[int index]
{
get
{
return names[index];
}
set
{
names[index] = value;
}
}
}
이제 Car의 insatnce인 NewCar을 NewCar[0] 이런 식으로 사용하면 names 멤버를 인덱스대로 사용할 수 있다.
반복기(iterator)는 배열과 컬렉션 형태의 데이터를 단계별로 실행하는 데 사용할 수 있다.
반복기를 구현할 때는 IEnumerable 인터페이스 (또는 IEnumerable<T> 인터페이스)와 yield 키워드를 사용한다.
class YieldReturn
{
static IEnumerable MultiData()
{
yield return "Hello";
yield return "World";
yield return "C#";
}
static void Main()
{
foreach (var item in MultiData())
{
Console.WriteLine(item);
}
}
}
실행하면
Hello
World
C#
요청이 올 때마다 하나씩만 반환된다는 걸 알 수 있다.
IEnumerable<T>로 컬렉션 형태의 데이터 반환받기> using System.Collections.Generic;
>
> // 1. not using yield
> static IEnumerable<int> Greater1(int[] numbers, int greater)
. {
. List<int> temp = new List<int>();
. foreach (var n in numbers)
. {
. if (n > greater)
. {
. temp.Add(n);
. }
. }
. return temp;
. }
> // 2. using yield
> static IEnumerable<int> Greater2(int[] numbers, int greater)
. {
. foreach(var n in numbers)
. {
. if (n > greater)
. {
. yield return n;
. }
. }
. }
>
> int[] numbers = { 1, 2, 3, 4, 5 };
>
> foreach (var n in Greater1(numbers, 3))
. {
. Console.WriteLine(n);
. }
4
5
> foreach(var n in Greater2(numbers, 3))
. {
. Console.WriteLine(n);
. }
4
5
yield return으로 반복해서 처리해야 하는 데이터를 반환할 때 유용하게 사용할 수 있다.
IEnumerable<T>의 MoveNext() 메서드와 Current 속성IEnumerable<T> 형태는 내부적으로 MoveNext() 메서드와 Current 속성을 사용한다.
> IEnumerable<int> GetNumbers()
. {
. yield return 1;
. yield return 2;
. yield return 5;
. }
>
> var nums = GetNumbers().GetEnumerator();
> nums
GetNumbers { 1, 2, 5 }
>
> nums.MoveNext();
> nums.Current
1
> nums.MoveNext()
true
> nums.Current
2
다음 값 이동, 반환하는 예제