[c#] DTO에 Nullable Reference Type을 사용하는 모범 사례

DynamoDB 테이블에서 읽어서 채워지는 DTO가 있습니다. 현재 다음과 같이 가정하십시오.

public class Item
{
    public string Id { get; set; } // PK so technically cannot be null
    public string Name { get; set; } // validation to prevent nulls but this doesn't stop database hacks
    public string Description { get; set; } // can be null
}

이를 처리하기 위해 개발할 모범 사례가 있습니까? 매개 변수가없는 생성자는 Dynamo SDK의 ORM (및 기타)에서 제대로 작동하지 않기 때문에 오히려 매개 변수가없는 생성자를 피하고 싶습니다.

PK public string Id { get; set; } = "";이기 때문에 결코 발생 Id하지 않으며 결코 null 이 될 수 없기 때문에 글을 쓰는 것이 이상하게 보입니다 . ""어쨌든 어떻게 되었더라도 어떤 용도로 사용 됩니까?

이에 대한 모범 사례는 무엇입니까?

  • 그들 string?중 일부는 결코 없어야하지만 null이 될 수 있다고 말해야합니다.
  • 나는 초기화해야 IdName함께 ""그들이 있기 때문에 해야 널 (null)이 쇼 결코 의도는 비록 ""사용되지 않을 것입니다.
  • 위의 일부 조합

참고 : 이것은 C # 8 nullable 참조 유형 에 관한 것입니다.



답변

옵션으로 default리터럴을null forgiving operator

public class Item
{
    public string Id { get; set; } = default!;
    public string Name { get; set; } = default!;
    public string Description { get; set; } = default!;
}

DTO가 DynamoDB에서 채워 지므로 MaybeNull/NotNull 사후 조건 속성 을 사용하여 null 가능성을 제어 할 수 있습니다

  • MaybeNull 널 입력 불가능 리턴 값은 널일 수 있습니다.
  • NotNull nullable 반환 값은 null이되지 않습니다.

그러나 이러한 특성은 주석이 달린 구성원의 발신자에 대한 Null 허용 분석에만 영향을줍니다. 일반적으로 이러한 속성을 메서드 반환, 속성 및 인덱서 게터에 적용합니다.

따라서 모든 속성을 null이 아닌 속성으로 간주하고 MaybeNull속성으로 장식하여 가능한 null값을 반환 함을 나타낼 수 있습니다

public class Item
{
    public string Id { get; set; } = "";
    [MaybeNull] public string Name { get; set; } = default!;
    [MaybeNull] public string Description { get; set; } = default!;
}

다음 예제는 업데이트 된 Item클래스 의 사용법을 보여줍니다 . 보시다시피, 두 번째 줄에는 경고가 표시되지 않지만 세 번째 줄에는 경고가 표시됩니다

var item = new Item();
string id = item.Id;
string name = item.Name; //warning CS8600: Converting null literal or possible null value to non-nullable type.

또는 모든 속성을 nullable 속성으로 만들고 NoNull반환 값을 표시 할 수 없습니다 null( Id예 :).

public class Item
{
    [NotNull] public string? Id { get; set; }
    public string? Name { get; set; }
    public string? Description { get; set; }
}

경고는 이전 예제와 동일합니다.

입력 매개 변수, 속성 및 인덱서 세터에 대한 AllowNull/DisallowNull 사전 조건 특성 도 비슷한 방식으로 작동합니다.

  • AllowNull 널 입력 불가능 입력 인수는 널일 수 있습니다.
  • DisallowNull 널 입력 가능 인수는 널이 아니어야합니다.

클래스가 데이터베이스에서 채워지기 때문에 도움이 될 것이라고 생각하지 않지만 첫 번째 옵션에서 이와 같이 속성 설정 기의 Null 허용 여부를 제어하는 ​​데 사용할 수 있습니다

[MaybeNull, AllowNull] public string Description { get; set; }

그리고 두 번째

[NotNull, DisallowNull] public string? Id { get; set; }

post / preconditions에 대한 몇 가지 유용한 세부 사항과 예제는이 devblog 기사 에서 찾을 수 있습니다.


답변

이 시나리오의 교과서 대답은을 사용하는 것입니다 string?당신을 위해 Id재산, 그러나 또한 로 장식 [NotNull]속성 :

public class Item
{
  [NotNull] public string? Id { get; set; }
  public string Name { get; set; }
  public string? Description { get; set; }
}

참조 : 에 따른 문서[NotNull]속성 “이 출력되지 않도록 지정은 대응하는 형태가 허용하는 경우에도 널 (null).”

그래서 여기서 정확히 무슨 일이 일어나고 있습니까?

  1. 첫째, string?반환 유형은 생성 중에 속성이 초기화되지 않았다는 컴파일러의 경고를 표시하지 않으므로 기본값 은로 설정 됩니다 null.
  2. 그런 다음 [NotNull]속성을 null이 아닌 변수에 속성을 할당하거나 참조를 해제하려고 할 때 경고를 방지합니다 . 실제로 컴파일러의 정적 흐름 분석 에 실제로이 속성은 절대로 적용되지 않습니다 null.

경고 : C #의 Null 허용 컨텍스트와 관련된 모든 경우와 마찬가지로 기술적으로 여전히 null값을 반환하지 못하게 하여 다운 스트림 예외가 발생할 가능성이 없습니다. 즉, 즉시 사용 가능한 런타임 유효성 검사가 없습니다. 모든 C #은 컴파일러 경고입니다. 소개 [NotNull]할 때 비즈니스 로직에 대한 힌트를 제공하여 경고를 효과적으로 무시합니다. 이와 같이, 당신이 가진 속성에 주석을 때 [NotNull], 당신은 당신의 약속에 대한 책임을 복용 “이 것 결코 부터 발생하지 IdPK가하고 null이 될 수 없다을.”

이러한 약속을 유지하기 위해 속성에 속성에 주석을 추가 할 수 있습니다 [DisallowNull].

public class Item
{
  [NotNull, DisallowNull] public string? Id { get; set; }
  public string Name { get; set; }
  public string? Description { get; set; }
}

참조 : 에 따른 문서[DisallowNull]속성 “지정 null입력으로 허용되고 대응하는 유형을 허용하더라도.”

값이 데이터베이스를 통해 할당되고 있기 때문에 이것은 귀하의 경우와 관련없는,하지만 [DisallowNull]혹시 대입 할 때 속성은 당신에게 경고를 줄 것이다 null에 (수) 값을 Id, 이 수의 반환 형식은 달리 할 수에도 불구하고 널 (null) . 이와 관련하여, Id작용 것이다 정확히 유사한 string반면, C #까지의 정적 흐름 분석뿐만 우려 같은 값이 대상의 구조와 특성 사이의 인구 초기화되지 않은 상태를 유지하도록 허용한다.

참고 : 다른 사람들이 언급했듯이 Id기본값 default!또는 중 하나를 기본값 으로 지정하면 사실상 동일한 결과를 얻을 수 있습니다 null!. 이것은 분명히 문체 선호도입니다. 더 명확하고 세부적인 제어 기능을 제공하기 때문에 Null 허용 주석을 사용하는 것을 선호하지만 !컴파일러를 종료하는 방법으로 남용하기 쉽습니다 . 값을 사용하여 속성을 명시 적으로 초기화하면 해당 값을 기본값으로 사용하지 않더라도 절대 사용하지 않을 것입니다.


답변

문자열은 참조 유형이며 항상 nullable이므로 특별한 작업을 수행 할 필요가 없습니다. 이 객체 유형을 다른 객체 유형에 매핑하려는 경우 나중에 문제가 발생할 수 있지만 나중에 처리 할 수 ​​있습니다.


답변