프로그래밍 언어/C#

[C#] default와 new() 제약조건 사용하기

ShovelingLife 2023. 8. 23. 10:54

generic 함수를 작성하는데, 기본 값을 생성해줘야 할 때가 있었다. 이 때 사용하게 되는 키워드가 new와 default다. 이 글에서는 class, struct 그리고 enum을 사용할 때 어떤 점을 주의해야 하는지 살펴보도록 하겠다.

new와 default

new는 generic 함수의 제약 조건이다. 해당 제약 조건을 걸면 generic 함수 내에서 new T()와 같은 코드를 사용할 수 있다. 이 제약 조건을 걸었을 경우 struct와 enum은 항상 사용가능하고, class는 구현에 따라 달라지게 된다.

default는 C#의 기본 키워드로 제약 조건이 필요하지도 않고, 다른 많은 곳에서도 사용할 수 있는 기능이다. default를 각 타입에 사용할 경우 아래 표와 같은 값이 생성된다.

typedefault value

class null
struct Struct { fields = default(type)… }
enum 0 (가장 처음 나오는 0인 item)

이제 각 타입의 기본값을 설정할 때 주의할 점을 살펴보도록 하겠다.

class

class는 참조 타입(reference type)이기 때문에 null을 허용한다. 그래서 default를 사용했을 때 값은 항상 null이 된다.

new의 경우 생성자를 하나도 만들지 않은 상태라면, 항상 사용이 가능하다(기본 생성자). 반대로 매개 변수가 있는 생성자만 있을 때는 사용할 수 없다.

using System;

public class ClassDefault1
{
    public int item1 { get; set; }
    public string item2;
}

public class ClassDefault2
{
    public int item1 { get; set; }
    public string item2;

    public ClassDefault2(int item1Value)
    {
        item1 = item1Value;
    }
}

public class Program
{
    public static TRefType GetDefault<TRefType>()
        where TRefType : new()
    {
        return new TRefType();
    }

    public static void Main(string[] args)
    {
        var cls = GetDefault<ClassDefault1>();
        // ClassDefault1 { item1 = 0, item2 = null }
        var cls2 = GetDefault<ClassDefault2>();
        // Error CS0310
    }
}

struct

struct는 값 타입(value type)이다. 참조가 아니기 때문에 null을 허용하지 않는다.

default의 값은 new 구문으로 생성한 값과 같다.

public struct StructDefault {
    public int item1 { get; set; }
    public string item2;

    public StructDefault(int i) {
        item1 = i;
        item2 = "";
    }
}

var str = default(StructDefault);
var str2 = new StructDefault());
// str, str2 == StructDefault { item1 = 0, item2 = null }

만약 default나 new로 생성하고자 한다면, 각 멤버에 기본 값을 설정해두면 된다.

public struct StructDefault {
    public int item1 { get; set; } = 10;
    public string item2 = "test";
}

var str = default(StructDefault);
// StructDefault { item1 = 10, item2 = "test" }

enum

enum은 struct와 같은 값 타입이다. default와 new를 사용할 때 항상 '(E)0’이 설정된다.

public enum EnumDefault1 {
    Item1,
    Item2,
    Item3,
    Item4,
}
public enum EnumDefault2 {
    Item1, // 0
    Item2, // 1
    Item3 = 0,
    Item4, // 1
}
public enum EnumDefault3 {
    Item1 = 1,
    Item2, // 2
    Item3, // 3
    Item4, // 4
}

var enm1 = default(EnumDefault1);
// EnumDefault1.Item1
var enm2 = default(EnumDefault2);
// EnumDefault2.Item1
var enm3 = default(EnumDefault3);
// 0

var newEnm = new EnumDefault1();
// EnumDefault1.Item1

EnumDefault3은 항목 중 0인 값이 없기 때문에 상수 0이 들어가게 된다. 이런 경우가 생길 수 있기 때문에 MSDN에서는 상수 0인 값이 항상 enum에 포함되는 것을 권장한다.

만약, enum만 사용가능한 generic 함수를 만들고 싶다면 아래와 같은 코드를 작성하면 된다.

public TEnum EnumDefault<TEnum>()
    where TEnum : struct, IConvertible
{
    if (!typeof(TEnum).IsEnum) 
   {
      throw new ArgumentException("TEnum must be an enumerated type");
   }
    return default(TEnum);
}

다만 이런 형태의 코드를 사용할 경우, 컴파일러 오류로 체크되지 않을 수 있다.

 

default와 new() 제약조건 사용하기 • CodeFIct