일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- sha
- RSA
- 2D Camera
- 이미지 폰트
- 가이드
- ui
- jumping ball
- Job 시스템
- base64
- unity
- 다이나믹 폰트
- 샘플
- adfit
- 암호화
- 단말기 해상도
- AES
- Unity Editor
- C#
- Custom Package
- DotsTween
- 프레임워크
- Framework
- Dynamic Font
- Dots
- Tween
- job
- 커스텀 패키지
- TextMeshPro
- 텍스트 메시 프로
- 최적화
- Today
- Total
EveryDay.DevUp
[Unity] 메서드 매개 변수 ( 값 형식, 참조 형식, in, out, ref, params ) 본문
매개 변수의 전달
1. 값 형식 매개 변수
- 값 형식의 매개 변수를 전달 할 경우 값 형식의 복사본이 만들어져 메서드에 전달되게 됨
public class ValueTypeSample
{
public int value = 0;
public void AddValue( int value )
{
value++;
Debug.Log(" AddValue : " + value);
}
}
public class MethodParameter : MonoBehaviour
{
private void Start()
{
ValueTypeSample valueTypeSample = new ValueTypeSample();
// 현재 value 값은 0
Debug.Log("value 1 : " + valueTypeSample.value);
// AddValue에서 매개변수로 전달받은 value에 +1을 하여 value가 1로 나옴
valueTypeSample.AddValue(valueTypeSample.value);
// 다시 value를 출력했을 때 값은 0, 값으로 전달한 매개변수는 값의 복사본이 전달되어
// 해당 함수에서만 값의 변경이 생김
Debug.Log("value 2 : " + valueTypeSample.value);
}
}
2. 참조 형식 매개 변수
- 참조형식의 매개 변수는 참조 중인 데이터를 가리키는 참조 값이 전달 됨
public class IntArraySample
{
int[] intArray = new int[3];
public void Start()
{
// 초기 값 0이 출력됨
Debug.Log("intArray index zero before : " + intArray[0]);
AddValueIndexZero(intArray);
// 1이 출력됨
// AddValueIndexZero의 참조 형식의 array를 매개변수로 전달했기 때문에 실제 값이 변경 됨
Debug.Log("intArray index zero after : " + intArray[0]);
}
void AddValueIndexZero( int[] arr )
{
arr[0]++;
}
}
3. in
- 참조 매개 변수, 매개 변수가 참조로 전달 되지만 수정은 할 수 없도록 전달 됨
- in 인수로 전달되기전 초기화되어야 함
- C# 7.2 이상에서 사용 가능
- 호출 메소드에 명시적으로 in을 사용하지 않아도 됨
- 매개 변수 한정자
1) 값 형식의 경우 값이 참조로 전달 되지만 값을 수정할 수는 없음
public struct ValueTypeSample2
{
public int value;
}
public class InSample
{
public void Start()
{
ValueTypeSample2 valueType = new ValueTypeSample2();
AddValueInValueType(in valueType);
ValueTypeSample2 valueType2;
// valueType2를 초기화 하지 않았기 때문에 에러가 발생함
AddValueInValueType(in valueType2);
}
void AddValueInValueType(in ValueTypeSample2 valueType)
{
// 값 형식 in의 경우 값을 변경할 수 없기 때문에 에러가 발생함
valueType.value = 1;
}
}
2) 참조 형식의 경우
public class InSample2
{
public void Start()
{
ReferenceTypeSample referenceTypeSample = new ReferenceTypeSample();
// 0 출력
Debug.Log(" referenceTypeSample value before: " + referenceTypeSample.value);
AddReferenceInValueType(in referenceTypeSample);
// 1 출력
Debug.Log(" referenceTypeSample value after: " + referenceTypeSample.value);
ReferenceTypeSample referenceTypeSample2;
// referenceTypeSample2를 초기화 하지 않았기 때문에 에러가 발생함
AddReferenceInValueType(in referenceTypeSample2);
}
void AddReferenceInValueType(in ReferenceTypeSample referenceTypeSample)
{
// 참조 형식의 in의 경우 내부의 값을 변경할 수 있음
referenceTypeSample.value = 1;
// 새로운 할당을 할 경우 에러가 발생함
referenceTypeSample = new ReferenceTypeSample();
}
}
4. out
- 출력 매개 변수, 매개 변수가 참조로 전달되며 메소드 안에서 작업이 수행되어 할당이 되어야 함
- out으로 전달되기 전 초기화하지 않아도 되며, 초기화 해도 해당 값은 무시 된다.
- 메서드 정의와 호출 메서드 모두 명시적으로 out 키워드를 사용해야 함
- 매개 변수 한정자
public class ReferenceTypeSample
{
public int value;
}
public class OutSample
{
public int value = 100;
public void Start()
{
// 출력 100
Debug.Log("before Value : " + value);
SetOutValue(out value);
// 출력 1
Debug.Log("after Value : " + value);
ReferenceTypeSample referenceTypeSample;
SetOutReferenceValue(out referenceTypeSample);
// 출력 10
Debug.Log("referenceTypeSample value : " + referenceTypeSample.value);
// 호출 함수에서 변수를 정의해서 사용할 수 있음
// C#7 이전에는 out 사용하기전 선언이 필요
SetOutReferenceValue(out ReferenceTypeSample referenceTypeSample2);
// 출력 10
Debug.Log("referenceTypeSample2 value : " + referenceTypeSample2.value);
// 필요 없는 경우 _로 생략이 가능함
SetTwoValue(out int a, out _);
}
void SetOutValue(out int outValue)
{
outValue = 1;
}
void SetOutReferenceValue( out ReferenceTypeSample referenceTypeSample)
{
referenceTypeSample = new ReferenceTypeSample();
referenceTypeSample.value = 10;
}
void SetTwoValue(out int a, out int b)
{
a = 1;
b = 2;
}
}
5. ref
- 참조 형식으로 값을 전달
1) 참조로 메소드 매개 변수 전달
- 메서드 정의와 호출 메서드 모두 명시적으로 ref를 사용해야 함
- 인수를 전달하기 전 초기화가 필요함
ㄱ. 값 형식에 사용할 경우
: 값 형식의 복사 대신 참조로 전달하여 메소드안에 값 변경이 값에 반영 됨
public class RefSampleValue
{
public int value = 0;
public void Start()
{
// 0 출력
Debug.Log(" value before : " + value);
AddValue(ref value);
// 1출력, 값 형식 value 값의 변경이 반영 됨
Debug.Log(" value before : " + value);
}
void AddValue( ref int value)
{
value++;
}
}
ㄴ. 참조 형식에 사용할 경우
: 참조 매개 변수가 참조하는 개체를 바꿀 수 있음
public class RefSampleReference
{
public void Start()
{
ReferenceTypeSample referenceTypeSample = new ReferenceTypeSample();
// 0 출력
Debug.Log(" referenceTypeSample value : " + referenceTypeSample.value);
SetValueNotRef(referenceTypeSample);
// 0 출력, 참조 형식의 매개 변수를 전달해도 참조하는 개체를 바꿀 수는 없음
Debug.Log(" referenceTypeSample value not ref : " + referenceTypeSample.value);
SetValueRef(ref referenceTypeSample);
// 5출력, ref를 사용할 경우 전달한 참조 형식의 참조 개체를 바꿀 수 있음
Debug.Log(" referenceTypeSample value ref : " + referenceTypeSample.value);
}
void SetValueNotRef(ReferenceTypeSample referenceTypeSample)
{
referenceTypeSample = new ReferenceTypeSample();
referenceTypeSample.value = 3;
}
void SetValueRef(ref ReferenceTypeSample referenceTypeSample)
{
referenceTypeSample = new ReferenceTypeSample();
referenceTypeSample.value = 5;
}
}
2) 참조 반환 값 ( ref return )
- 메소드가 호출자에게 참조로 반환
- C#7 이상 사용 가능
struct MassiveStruct
{
public int massive;
}
struct MassiveData
{
public MassiveStruct[] massiveDataArray;
public MassiveData(int size)
{
massiveDataArray = new MassiveStruct[size];
}
public MassiveStruct GetMassiveStructByIndex(int index)
{
return massiveDataArray[index];
}
public ref MassiveStruct GetRefMassiveStructByIndex( int index)
{
return ref massiveDataArray[index];
}
}
public class RefReturnSample
{
public void Start()
{
MassiveData massiveData = new MassiveData(1);
MassiveStruct massiveStruct = massiveData.GetMassiveStructByIndex(0);
massiveStruct.massive = 10;
// 0 이 출력됨, 값의 복사 값이 return 되었기 때문에 massiveData에 영향을 주지 않음
Debug.Log("massiveData Index " + massiveData.massiveDataArray[0].massive);
ref MassiveStruct refMassiveStruct = ref massiveData.GetRefMassiveStructByIndex(0);
refMassiveStruct.massive = 10;
// 10이 출력 됨, 참조 값이 return 되었기 때문에 massiveData에 영향을 줌
// 큰 구조체 데이터 값을 return할 경우의 성능 상 이점이 있을 수 있음
Debug.Log("ref massiveData Index " + massiveData.massiveDataArray[0].massive);
}
}
3) 참조 로컬
- 참조 로컬은 return ref를 사용하여 반환된 값을 참조하는 데 사용
- 비 참조 반환값으로 초기화할 수 없음
- 변수 선언 앞과 값을 참조로 반환하는 메소드 호출 바로 앞에 ref를 명시함으로써 사용
public class RefReturmSample2
{
public void Start()
{
ReferenceTypeSample sample1 = new ReferenceTypeSample();
ReferenceTypeSample sample2 = GetReferenceSampe(ref sample1);
// 100, 100 출력
Debug.Log(" sample value :" + sample1.value + " sample2 value :" + sample2.value);
sample2 = new ReferenceTypeSample();
sample2.value = 200;
// 100, 200 출력, sample2가 가리키는 참조 개체가 새로 할당됬기 때문에 sample1과 sample2가 달라짐
Debug.Log(" sample value :" + sample1.value + " sample2 value :" + sample2.value);
ref ReferenceTypeSample sample3 = ref GetRefReferenceSampe(ref sample1);
// 100, 100 출력
Debug.Log(" sample value :" + sample1.value + " sample3 value :" + sample3.value);
sample3 = new ReferenceTypeSample();
sample3.value = 200;
// 200, 200 출력, sample3과 sample1은 동일한 참조를 가리키고 있기 때문에 같은 값을 출력함
Debug.Log(" sample value :" + sample1.value + " sample3 value :" + sample3.value);
}
public ReferenceTypeSample GetReferenceSampe(ref ReferenceTypeSample sample)
{
sample = new ReferenceTypeSample();
sample.value = 100;
return sample;
}
public ref ReferenceTypeSample GetRefReferenceSampe(ref ReferenceTypeSample sample)
{
sample = new ReferenceTypeSample();
sample.value = 100;
return ref sample;
}
}
5) 참조 읽기 전용 로컬 readonly 참조 로컬
- readonly를 통해 참조 로컬을 수정할 수 없도록 함
- 값 형식의 복사로 인한 오버헤드 문제를 해결하기 위함
- 2)의 행위로 반환 된 값 참조를 변경하지 않도록 하기 위해 사용
public struct ReadOnlySampleStruct
{
public int value;
public void AddValue()
{
value++;
}
}
public class ReadOnlySample2
{
ReadOnlySampleStruct readOnlySampleStruct = new ReadOnlySampleStruct();
public void Start()
{
ref readonly ReadOnlySampleStruct rost = ref GetSampleStruct(ref readOnlySampleStruct);
// ref readonly 이기 때문에 값을 수정하려고할 경우 에러가 발생함
rost.value = 2;
// defensive copy가 발생함
rost.AddValue();
}
ref readonly ReadOnlySampleStruct GetSampleStruct( ref ReadOnlySampleStruct readOnlySampleStruct)
{
return ref readOnlySampleStruct;
}
}
ㄱ) defensive copy
: readonly struct에서 struct 멤버의 값을 내부 함수로 변경하려고할 경우 컴파일러에서 이를 방지하기 위해 복사본으로 함수를 진행
public struct ReadOnlySampleStruct
{
public int value;
public void AddValue()
{
//아래와 같이 함수가 실행됨
//ReadOnlySampleStruct temp = new ReadOnlySampleStruct();
//temp.value++;
value++;
}
}
public class ReadOnlySample
{
readonly ReadOnlySampleStruct readOnlySampleStruct = new ReadOnlySampleStruct();
public void Start()
{
// error - readonly 구조체로 value에 직접적인 수정이 불가능함
readOnlySampleStruct.value = 1;
// 0 출력
Debug.Log(" value 1: " + readOnlySampleStruct.value);
readOnlySampleStruct.AddValue();
// 1이 출력될 것 같지만, 0이 출력됨. readonly struct로 정의되어 있기 때문에
// 컴파일러에서 struct.value를 수정하지 않도록 하기 위해 복사본을 만들어서 value++을 함
// readOnlySampleStruct의 값은 변경되지 않음 이를 defensvie copy 라함
Debug.Log(" value 2: " + readOnlySampleStruct.value);
}
}
ㄴ) readonly struct
: defensive copy문제를 해결하기 위해 struct를 readonly로 정의
// readonly struct는 멤버도 reaonly로 정의되기 때문에 값 변경이 명시적으로 불가능함을 알 수 있음
// defensive copy는 코드에서 에러가 나지 않기 때문에 프로그래머가 해당 로직을 알지 못하면 원인 파악이 힘들 수 있음
public readonly struct ReadOnlySampleStruct2
{
public readonly int value;
public int AddValue()
{
int addValue = value;
addValue++;
return addValue;
}
}
* ref, in, out 사용 시 이슈
ㄱ) 메서드 오버로딩 제한
public class MethodOverloading
{
public void Add(int i) { }
public void Add(ref int i) { }
// error - ref, in, out 중 하나만을 사용해서 함수 오버로딩을 사용할 수 있음.
// ref 로 Add의 인수가 하나인 함수를 이미 정의 했기 때문에 in을 사용해서 함수 오버로딩을 할 수 없음
public void Add(in int i) { }
// 매개변수의 개수가 다르면 오버로딩할 수 있음
public void Add(ref int i, ref int j) { }
// error - 매개 변수의 개수가 동일하고 ref, in, out으로 정의가 된 함수가 있기 때문에 오버로딩을 할 수 없음
public void Add(out int i, ref int j) { }
}
ㄴ) async 한정자를 사용하여 정의하는 비동기 메소드에는 사용할 수 없음
ㄷ) yield return 또는 yield break문을 포함하는 반복기 메소드에는 사용할 수 없음
ㄹ) 확장 메서드의 첫번째 인수에는 out을 사용할 수 없음
ㅁ) 인수가 구조체가 아니거나, 구조체로 제한되지 않는 제네릭 형식의 경우 확장 메소드 첫번째 인수에 ref 키워드를 사용할 수 없음
ㅂ) 첫번째 인수가 구조체인 경우 이외에는 in키워드를 사용할 수 없고, 구조체로 제한되는 경우에는 제네릭 형식에 in 키워드를 사용할 수 없음
6. params
- 가변 개수의 인수를 사용할 수 있도록 함
- params 키워드 뒤에는 추가 매개 변수를 사용할 수 없음
- 1차원 배열이 아닐 경우 컴파일 오류가 발생
public class ParamsSample
{
void Start()
{
// 매개 변수가 가변적이기 때문에 동일 함수를 사용할 수 있음
Add(1);
Add(2, 3);
}
public void Add( params int[] values) {}
}
* 출처 및 참고 블로그
https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/method-parameters
https://www.csharpstudy.com/Latest/CS7-outvar.aspx
https://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0&detail=1&wid=11526
https://www.sysnet.pe.kr/2/0/11524
https://www.sysnet.pe.kr/2/0/11525
'C#' 카테고리의 다른 글
[Unity] 특성 ( Attribute Class ) - 1 (0) | 2022.07.07 |
---|---|
[Unity] .Net 디컴파일 ( JetBrain DotPeek ) (0) | 2021.12.30 |
[Unity] 액세스 한정자 ( public, protected, internal, private ) (2) | 2021.12.27 |
[Unity] Unity의 버전별 C# 지원 버전 (0) | 2021.12.24 |
[Unity] Struct (구조체) (0) | 2021.12.23 |