함수가 null 또는 빈 객체를 반환해야합니까?


209

함수에서 데이터를 반환 할 때 가장 좋은 방법 은 무엇입니까? Null 또는 빈 객체를 반환하는 것이 더 낫습니까? 그리고 왜 하나는 다른 하나를해야합니까?

이걸 고려하세요:

public UserEntity GetUserById(Guid userId)
{
     //Imagine some code here to access database.....

     //Check if data was returned and return a null if none found
     if (!DataExists)
        return null; 
        //Should I be doing this here instead? 
        //return new UserEntity();  
     else
        return existingUserEntity;
}

이 프로그램에는 해당 GUID가있는 데이터베이스에 사용자 정보가없는 유효한 사례가 있다고 가정합니다. 이 경우 예외를 던지는 것이 적절하지 않다고 생각할까요? 또한 예외 처리로 인해 성능이 저하 될 수 있다는 인상을 받고 있습니다.


4
나는 당신이 의미하는 것 같아요 if (!DataExists).
Sarah Vessels

107
이것은 건축 문제이며 완벽하게 적합합니다. OP의 질문은 해결하려는 비즈니스 문제에 관계없이 유효합니다.
Joseph Ferris

2
이 질문은 이미 충분히 대답되었습니다. 나는 이것이 매우 흥미로운 질문이라고 생각합니다.
John Scipione

'getUser ()'는 null을 반환해야합니다. OTOH의 'getCurrentUserInfo ()'또는 'getCurrentPermissions ()'는 더 많은 질문을 공개 할 것 입니다. 누가 / 누구든지 로그인했는지 여부에 관계없이 아닌 응답 객체를 반환해야합니다 .
Thomas W

2
@Bergi 아니오 다른 하나는 중복입니다. 광산은 10 월에 처음으로, 다른 하나는 12 월에 3 마리의 나방에 물었다. 다른 하나는 약간 다른 컬렉션에 대해 이야기합니다.
7wp

답변:


207

사용 가능한 데이터가 없음을 나타내려는 경우 일반적으로 null을 반환하는 것이 가장 좋습니다.

빈 객체는 데이터가 반환되었음을 나타내며 null을 반환하면 아무것도 반환되지 않았 음을 나타냅니다.

또한 null을 반환하면 객체의 멤버에 액세스하려고하면 null 예외가 발생하여 버그가있는 코드를 강조 표시하는 데 유용 할 수 있습니다. 빈 개체의 멤버에 액세스해도 버그가 발견되지 않을 수 있습니다.


21
문제를 삼키지 않고 null을 반환하지 않고 예외를 처리해야합니다. 최소한 이것을 기록한 다음 계속해야합니다.
Chris Ballance

130
@Chris : 동의하지 않습니다. 코드에서 반환 값이 null임을 명확하게 문서화하면 조건과 일치하는 결과가 없으면 null을 반환하는 것이 좋습니다. 예외를 던지는 것이 첫 번째가 아닌 마지막 선택이어야합니다.
Mike Hofer

12
@Chris : 어떤 기준으로 결정합니까? 방정식에 벌목을 추가하는 것은 과도 하게 보입니다. 소비 코드가 사용자가없는 경우 수행해야 할 작업을 결정하게합니다. 이전의 의견과 마찬가지로, "데이터 없음"으로 보편적으로 정의 된 값을 반환하는 데는 아무런 문제가 없습니다.
Adam Robinson

17
Microsoft 개발자가 "null을 반환하는 것은"문제를 삼키는 것과 같다 "고 생각합니다. 메모리가 제공되는 경우, 호출자의 요청과 일치하는 것이 없으면 null이 반환되는 프레임 워크에 많은 메서드가 있습니다. "문제를 삼키는 것입니까?"
Mike Hofer

5
마지막으로 bool GetUserById(Guid userId, out UserEntity result)"널"리턴 값을 선호하고 예외를 던지는 것만 큼 극단적이지는 않습니다. 그것은 null같은 아름다운 무료 코드를 허용 if(GetUserById(x,u)) { ... }합니다.
Marcel Jackwerth가

44

귀하의 사례에 가장 적합한 것이 무엇인지에 달려 있습니다.

"이러한 사용자가 존재하지 않습니다"와 같이 null을 반환하는 것이 합리적입니까?

아니면 기본 사용자를 만드는 것이 합리적입니까? 이것은 사용자가 존재하지 않는 경우 호출 코드는 사용자가 요청할 때 존재하기를 원한다고 안전하게 가정 할 수있을 때 가장 의미가 있습니다.

또는 호출 코드가 잘못된 ID를 가진 사용자를 요구하는 경우 예외 ( "FileNotFound")를 발생시키는 것이 합리적입니까?

그러나 우려 / SRP 관점의 분리에서 처음 두 가지가 더 정확합니다. 그리고 기술적으로 첫 번째는 가장 올바른 (하지만 머리로) - 사용자를 얻기 - GetUserById는 단 한 가지에 대한 책임을 져야한다. 다른 것을 반환하여 자체 "사용자가 존재하지 않음"사례를 처리하면 SRP를 위반할 수 있습니다. 다른 검사로 분리- bool DoesUserExist(id)예외를 던지도록 선택한 경우 적절합니다.

아래의 광범위한 의견을 바탕으로 : API 수준의 디자인 질문 인 경우이 방법은 "OpenFile"또는 "ReadEntireFile"과 유사 할 수 있습니다. 우리는 일부 저장소에서 사용자를 "열고"결과 데이터에서 개체를 수화하고 있습니다. 이 경우 예외 적절할 수 있습니다 . 아닐 수도 있지만 가능할 수도 있습니다.

모든 접근 방식이 허용됩니다. API / 응용 프로그램의 더 큰 컨텍스트에 따라 다릅니다.


누군가 당신에게 투표를했고 나는 당신에게 다시 투표를했습니다. 왜냐하면 이것은 나에게 나쁜 대답처럼 보이지 않기 때문입니다. 예외 : 포스터가 제공하는 것과 같은 방법으로 사용자를 찾을 때 예외를 throw하지 않습니다. 아무 사용자를 찾을 수없는 것은 잘못된 ID 또는 일부 같은 예외 가치 문제를 의미하는 경우 그 이상까지 일한다 - 던지는 방법의 요구보다 그 ID는 등 어디에서 왔는지에 대해 알아야 할 사항
야곱 Mattison

(공포는 이런 상황에서 예외를 던지는 아이디어에 대한 반대라고 생각합니다.)
Jacob Mattison

1
마지막 지점까지 동의했습니다. 일반적으로 "데이터 없음"으로 정의 된 값을 반환하여 SRP를 위반하지 않습니다. where 절이 결과를 생성하지 않으면 SQL 데이터베이스가 오류를 반환해야한다고 말하는 것과 같습니다. 예외는 유효한 디자인 선택이지만 (소비자에게 자극을 주지만) null을 반환하는 것보다 "정확한"것은 아닙니다. 그리고 아니요, 저는 DV가 아닙니다.
Adam Robinson

@JacobM 우리는 존재하지 않는 파일 시스템 경로를 요구할 때 예외를 발생시킵니다. 그래서 분명히 두 가지 모두 적절합니다. 이것은 내가 얻는 것입니다.
Rex M

2
@Charles : 당신은 "어느 시점에서 예외를 던져야한다"라는 질문에 대답하고 있지만,이 함수는 "이 함수가 예외를 던져야 하는가"입니다. 정답은 "예"가 아니라 "아마도"입니다.
Adam Robinson

30

개인적으로 NULL을 사용합니다. 반환 할 데이터가 없다는 것이 분명합니다. 그러나 Null 개체 가 유용한 경우 가 있습니다.


이것을 직접 답변으로 추가하려고합니다. NullObjectPattern 또는 특수 사례 패턴. 그런 다음 NoUserEntitiesFound, NullUserEntities 등 각 사례에 대해 하나씩 구현할 수 있습니다.
David Swindells

27

반환 유형이 배열이면 빈 배열을 반환하고 그렇지 않으면 null을 반환합니다.


현재 목록의 0 개 항목이 할당되지 않은 목록과 동일합니까?
AnthonyWJones

3
목록에있는 0 개의 항목이와 동일하지 않습니다 null. foreach걱정없이 문과 linq 쿼리 에 사용할 수 있습니다 NullReferenceException.
Darin Dimitrov

5
이것이 더 이상지지되지 않은 것에 놀랐습니다. 이것은 나에게 꽤 합리적인 지침처럼 보입니다.
shashi

빈 컨테이너는 null 객체 패턴의 특정 인스턴스 일뿐입니다. 어느 것이 적절한 지 알 수 없습니다.
중복 제거기

데이터를 사용할 수 없을 때 빈 배열을 반환하는 것은 단순히 잘못 입니다. 사용 가능한 데이터와 포함하지 않는 데이터 및 사용 불가능한 데이터간에 차이가 있습니다. 두 경우 모두 빈 배열을 반환하면 어떤 경우인지 알 수 없습니다. 당신도이 존재하는 데이터가 바보 넘어 여부를 확인하지 않고 foreach 문을 사용할 수 있습니다 만, 그래서 그 일을 - 호출자가 해야 발신자가하는 경우 데이터가 존재하고 NullReferenceException이 있는지 여부를 확인하지 검사가 좋은 이 버그를 노출하기 때문에 ..
복원 Monica Monica

12

특정 계약이 위반 된 경우 예외를 처리해야합니다.
특정 예에서 알려진 ID를 기반으로 UserEntity를 요청하면 누락 된 (삭제 된) 사용자가 예상되는 경우에 따라 다릅니다. 그렇다면 반환 null하지만 예상되는 경우가 아니면 예외를 throw하십시오.
함수가 호출 UserEntity GetUserByName(string name)되면 아마도 throw되지 않고 null을 반환합니다. 두 경우 모두 빈 UserEntity를 반환하면 도움이되지 않습니다.

문자열, 배열 및 컬렉션의 경우 일반적으로 상황이 다릅니다. 메서드가 null'빈'목록으로 수락해야 하지만 길이가 0이 아닌 컬렉션을 반환 해야한다는 MS 지침 양식을 기억합니다 null. 문자열도 마찬가지입니다. 빈 배열을 선언 할 수 있습니다.int[] arr = new int[0];


빈 문자열을 반환할지 여부를 결정할 때 Google에서이를 보여 주었으므로 문자열이 다릅니다.
누 메논

문자열, 컬렉션 및 배열은 다르지 않습니다 . MS가 그렇게 말한다면 MS는 틀린 것입니다. 빈 문자열과 null과 빈 컬렉션과 null 사이에는 차이가 있습니다. 두 경우 모두 전자는 기존 데이터 (크기 0)를 나타내고 후자는 데이터 부족을 나타냅니다. 어떤 경우에는 구별이 매우 중요합니다. 예를 들어, 캐시에서 항목을 조회하는 경우 캐시되지는 않지만 비어있는 데이터와 캐시되지 않은 데이터의 차이를 파악하여 기본 데이터 소스에서 가져와야 할 수도 있습니다. 빈.
Reinstate Monica

1
요점과 맥락을 놓친 것 같습니다. .Wher(p => p.Lastname == "qwerty")이 아닌 빈 컬렉션을 반환해야합니다 null.
Henk Holterman

@HenkHolterman 전체 컬렉션에 액세스하고 컬렉션의 항목을 허용하지 않는 필터를 적용 할 수 있으면 빈 컬렉션이 올바른 결과입니다. 그러나 전체 컬렉션이 존재하지 않으면 빈 컬렉션은 매우 오해의 소지가 있습니다. 상황이 정상인지 예외인지에 따라 null 또는 던지는 것이 정확합니다. 귀하의 게시물이 어떤 상황에 대해 이야기하고 있는지 (그리고 지금은 이전 상황에 대해 이야기하고 있음을 분명히하고) OP가 후자에 대해 이야기하고 있기 때문에 귀하와 동의하지 않아야합니다.
Reinstate Monica

11

이것은 특정 Guid Id를 가진 사용자의 존재가이 기능의 정상적인 사용 사례인지 또는이 방법이 사용자에게 제공하는 기능을 응용 프로그램이 성공적으로 완료하지 못하게하는 이상 여부에 따라 비즈니스 질문입니다. 반대하다 ...

"예외"인 경우 해당 ID를 가진 사용자가 없으면 응용 프로그램이 수행하는 모든 기능을 성공적으로 완료하지 못할 수 있습니다 (우리는 제품을 배송 한 고객에 대한 송장을 만드는 중입니다 ... )이 경우 ArgumentException (또는 다른 사용자 정의 예외)이 발생합니다.

누락 된 사용자가 정상인 경우 (이 함수를 호출했을 때 발생할 수있는 정상적인 결과 중 하나) null을 반환합니다.

편집 : (다른 답변에서 Adam의 의견을 다루기 위해)

응용 프로그램에 여러 비즈니스 프로세스가 포함 된 경우 (하나 이상이 성공적으로 완료하기 위해 사용자를 필요로하고, 하나 이상이 사용자없이 성공적으로 완료 될 수있는 경우) 예외는 호출 스택에서 더 멀리 던져야합니다. 사용자를 필요로하는 비즈니스 프로세스는이 실행 스레드를 호출합니다. 이 메소드와 그 시점 (예외가 발생하는 곳) 사이의 메소드는 사용자가 존재하지 않는다는 것을 전달해야합니다 (null, boolean 등-구현 세부 사항 임).

그러나 응용 프로그램 내의 모든 프로세스 에 사용자가 필요한 경우이 방법에서 여전히 예외가 발생합니다 ...


downvoter에 -1, Charles에 +1-전적으로 비즈니스 문제이며 이에 대한 모범 사례는 없습니다.
Austin Salonen

시내를 건너고 있습니다. "오류 조건"인지 여부는 비즈니스 논리에 의해 결정됩니다. 이를 처리하는 방법은 애플리케이션 아키텍처 결정입니다. 비즈니스 로직은 요구 사항이 충족된다는 것만으로 널 (null)이 리턴되도록 지시하지 않습니다. 비즈니스가 메소드 리턴 유형을 결정하는 경우 구현의 기술적 측면에 너무 관여합니다.
Joseph Ferris

"구조적 예외 처리"의 핵심 원리 인 @joseph는 메소드가 구현하도록 코딩 된 함수를 메소드가 완료 할 수없는 경우 예외를 처리해야한다는 것입니다. 이 메소드가 구현하도록 코딩 된 비즈니스 기능이 "성공적으로 완료"될 수있는 경우 (도메인 모델에서 의미하는 것) 예외를 던질 필요가 없으면 널을 리턴 할 수 있습니다. 또는 부울 "FoundUser"변수 또는 기타 ... 사용자를 찾을 수없는 호출 메소드와 통신하는 방법은 기술적 구현 세부 사항이됩니다.
Charles Bretana

10

개인적으로 null을 반환합니다. DAL / 리포지토리 레이어가 작동하는 방식이기 때문입니다.

존재하지 않으면 성공적으로 객체를 가져 오는 것으로 해석 될 수있는 것을 반환하지 마십시오 null. 여기서 아름답게 작동합니다.

가장 중요한 것은 DAL / Repos Layer 전체에 일관성을 유지하는 것인데, DAL / Repos Layer를 사용하는 방법에 혼란을주지 않습니다.


7

나는하는 경향이있다

  • return null불명 인 상태의 개체 ID가 존재하지 않는 경우는 사전에인지 해야 존재한다.
  • throw존재 해야 할 때 객체 ID가 존재하지 않는 경우 .

이 두 가지 시나리오를이 세 가지 유형의 방법으로 차별화합니다. 먼저:

Boolean TryGetSomeObjectById(Int32 id, out SomeObject o)
{
    if (InternalIdExists(id))
    {
        o = InternalGetSomeObject(id);

        return true;
    }
    else
    {
        return false;
    }
}

둘째:

SomeObject FindSomeObjectById(Int32 id)
{
    SomeObject o;

    return TryGetObjectById(id, out o) ? o : null;
}

제삼:

SomeObject GetSomeObjectById(Int32 id)
{
    SomeObject o;

    if (!TryGetObjectById(id, out o))
    {
        throw new SomeAppropriateException();
    }

    return o;
}

당신은 의미 out하지ref
마 엘렌에게

@ 매트 : 예, 선생님, 가장 확실합니다! 결정된.
Johann Gerell

2
실제로 이것은 유일한 단일 크기의 답변이며 따라서 전적으로 유일한 진리입니다! :) 그렇습니다 . 메소드가 호출되는 가정 에 따라 다릅니다 ... 먼저 이러한 가정을 정리하고 위의 올바른 조합을 선택하십시오. 여기에 도착하기에는 너무 많이 스크롤했습니다 :) +100
yair

비동기 메소드를 지원하지 않는 것을 제외하고는 사용하는 패턴 인 것 같습니다. 이 답변을 참조하고 Tuple Literals-> here
tutugates

6

또 다른 접근 방식은 콜백 객체 또는 값에서 작동하는 델리게이트를 전달하는 것입니다. 값을 찾을 수 없으면 콜백이 호출되지 않습니다.

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

이것은 코드 전체에서 null 검사를 피하고 값을 찾지 못하면 오류가 아닌 경우에 효과적입니다. 특별한 처리가 필요한 경우 객체를 찾을 수 없을 때 콜백을 제공 할 수도 있습니다.

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

단일 객체를 사용하는 동일한 접근 방식은 다음과 같습니다.

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

디자인 관점에서 저는이 접근 방식이 정말 마음에 들지만 일류 함수를 쉽게 지원하지 않는 언어로 콜 사이트를 더 크게 만드는 단점이 있습니다.


흥미 롭군 델리게이트에 대해 이야기하기 시작했을 때 나는 즉시 Lambda 표현이 여기에 사용될 수 있는지 궁금해하기 시작했습니다.
7wp

예! 내가 이해하는 것처럼 C # 3.0과 람다 구문은 기본적으로 익명 대의원을위한 구문 설탕입니다. Java에서와 마찬가지로 멋진 람다 또는 익명 대리자 구문이 없으면 간단히 익명 클래스를 만들 수 있습니다. 조금 더 추악하지만 실제로는 매우 유용 할 수 있습니다. 요즘 C # 예제에서 명명 된 대리자 대신 Func <UserEntity> 또는 이와 유사한 것을 사용할 수 있었지만 마지막 C # 프로젝트는 여전히 버전 2를 사용하고 있다고 가정합니다.
Marc

+1이 방법이 마음에 듭니다. 그러나 문제는 이것이 기존이 아니며 코드베이스에 대한 진입 장벽을 약간 증가 시킨다는 것입니다.
timoxley 2009

4

우리는 CSLA.NET을 사용하며, 실패한 데이터 반입은 "빈"객체를 반환해야한다는 관점을 취합니다. 이 여부를 확인하는 규칙 요구 이것은 실제로 매우 성가신 obj.IsNew보다 rathern을obj == null .

이전 포스터에서 언급했듯이 null 반환 값을 사용하면 코드가 즉시 실패하여 빈 객체로 인한 스텔스 문제가 발생할 가능성이 줄어 듭니다.

개인적 null으로 더 우아 하다고 생각 합니다.

매우 일반적인 경우이며, 여기 사람들이 놀라워하는 것에 놀랐습니다. 어떤 웹 응용 프로그램에서든 쿼리 문자열 매개 변수를 사용하여 데이터를 가져 오는 경우가 종종 있습니다. ".

다음과 같이 처리 할 수 ​​있습니다.

if (User.Exists (id)) {
  this.User = User.Fetch (id);
} else {
  Response.Redirect ( "~ / notfound.aspx");
}

... 매번 데이터베이스에 대한 추가 호출이므로 트래픽이 많은 페이지에서 문제가 될 수 있습니다. 이므로:

this.User = User.Fetch (id);

if (this.User == null) {
  Response.Redirect ( "~ / notfound.aspx");
}

... 한 번의 통화 만 필요합니다.


4

nullnull 병합 연산자 ( ??) 와 호환되므로을 선호합니다 .


4

빈 객체 대신 null을 반환한다고 말하고 싶습니다.

그러나 여기서 언급 한 특정 인스턴스는 해당 사용자의 키와 같은 사용자 ID별로 사용자를 검색하고 있습니다.이 경우 사용자 인스턴스 인스턴스가 없으면 예외를 던지려고합니다. .

이것이 내가 일반적으로 따르는 규칙입니다.

  • 기본 키 조작으로 찾기에서 결과를 찾을 수 없으면 ObjectNotFoundException을 처리하십시오.
  • 다른 기준으로 찾은 결과가 없으면 null을 반환합니다.
  • 키가 아닌 기준으로 찾기에서 결과를 찾지 못하면 여러 객체를 반환 할 수 있으며 빈 컬렉션을 반환합니다.

이러한 경우에 왜 예외를 던지겠습니까? 때로는 사용자가 데이터베이스에 존재하지 않으므로 이러한 상황이 발생하지 않을 수 있습니다. 예외적 인 행동은 아닙니다.
siride

3

컨텍스트에 따라 다르지만 (예와 같이) 하나의 특정 객체를 찾고 있으면 일반적으로 null을 반환하고 일련의 객체를 찾고 있지만 아무것도없는 경우 빈 컬렉션을 반환합니다.

코드에서 실수를하고 null을 반환하면 null 포인터 예외가 발생하면 더 빨리 잡을 수 있습니다. 빈 개체를 반환하면 처음 사용하면 작동하지만 나중에 오류가 발생할 수 있습니다.


+1 나는 당신이 여기서 말하는 것과 같은 논리에 의문을
품고 있었기

3

이 경우 최고는 해당 사용자가없는 경우 "널"을 리턴합니다. 또한 메소드를 정적으로 만드십시오.

편집하다:

일반적으로 이와 같은 메소드는 일부 "User"클래스의 멤버이며 해당 인스턴스 멤버에 액세스 할 수 없습니다. 이 경우 메서드는 정적이어야하며, 그렇지 않으면 "User"인스턴스를 만든 다음 GetUserById 메서드를 호출하여 다른 "User"인스턴스를 반환해야합니다. 혼란 스럽습니다. 그러나 GetUserById 메소드가 일부 "DatabaseFactory"클래스의 멤버 인 경우 인스턴스 멤버로 두는 데 아무런 문제가 없습니다.


왜 메소드를 정적으로 만들고 싶은지 물어볼 수 있습니까? Dependency Injection을 사용하려면 어떻게해야합니까?
7wp

이제 당신의 논리를 얻습니다. 그러나 리포지토리 패턴을 고수하고 리포지토리에 종속성 주입을 사용하여 정적 메서드를 사용할 수 없습니다. 그러나 null 반환을 제안하는 +1 :)
7wp

3

개인적으로 객체의 기본 인스턴스를 반환합니다. 그 이유는 메소드의 목적에 따라 메소드가 0에서 다수 또는 0에서 1로 리턴되기를 기대하기 때문입니다. 이 접근법을 사용하여 모든 종류의 오류 상태가되는 유일한 이유는 메소드가 객체를 반환하지 않았고 항상 (1 대 다수 또는 단일 반환의 관점에서) 예상 된 경우입니다.

이것이 비즈니스 영역 문제라는 가정에 관해서는 방정식의 측면에서 볼 수는 없습니다. 반환 유형의 정규화는 유효한 응용 프로그램 아키텍처 질문입니다. 최소한 코딩 관행의 표준화 대상이됩니다. "시나리오 X에서 널 (null)을 주면된다"고 말하는 비즈니스 사용자가 있는지 의심합니다.


+1 문제의 다른 견해를 좋아합니다. 따라서 기본적으로 응용 프로그램 전체에서 방법이 일관된 한 내가 선택한 접근 방식 중 어느 것이 좋을까요?
7wp

1
그것이 나의 믿음입니다. 일관성이 매우 중요하다고 생각합니다. 여러 장소에서 여러 가지 방식으로 작업을 수행하면 새로운 버그가 발생할 위험이 높아집니다. 우리는 개인적으로 기본 객체 접근 방식을 사용했습니다. 도메인 모델에서 사용하는 Essence 패턴과 잘 작동하기 때문입니다. 모든 도메인 객체에 대해 테스트하여 채워 졌는지 여부를 알려주는 단일 일반 확장 방법이 있으므로 objectname.IsDefault () 호출로 모든 DO를 테스트 할 수 있음을 알 수 있습니다. .
Joseph Ferris

3

비즈니스 오브젝트에는 두 가지 주요 Get 메소드가 있습니다.

상황을 단순하게 유지하거나 질문하면 다음과 같습니다.

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

첫 번째 방법은 특정 엔티티를 가져올 때 사용되며 두 번째 방법은 웹 페이지에서 엔티티를 추가하거나 편집 할 때 특히 사용됩니다.

이를 통해 우리는 두 세계가 사용되는 상황에서 최상의 결과를 얻을 수 있습니다.


3

저는 프랑스의 IT 학생이므로 영어 실력이 떨어집니다. 우리의 클래스에서 우리는 그러한 메소드가 null 또는 빈 객체를 반환해서는 안된다는 말을 듣고 있습니다. 이 메소드의 사용자는 먼저 찾고자하는 오브젝트가 있는지 확인한 후 가져 오려고합니다.

Java를 사용 assert exists(object) : "You shouldn't try to access an object that doesn't exist";하여 "전제 조건"을 표현하기 위해 null을 반환 할 수있는 모든 메소드의 시작 부분에 a를 추가해야합니다 (영어로 된 단어가 무엇인지 모르겠습니다).

IMO 이것은 실제로 사용하기 쉽지 않지만 그것이 내가 사용하고있는 것이기 때문에 더 나은 것을 기다리고 있습니다.


1
답변 주셔서 감사합니다. 그러나 나는 그것이 존재하는지 먼저 확인하는 아이디어를 싫어합니다. 데이터베이스에 추가 쿼리를 생성하기 때문입니다. 하루에 수백만 명의 사람들이 액세스하는 응용 프로그램에서는 성능이 크게 저하 될 수 있습니다.
7wp

1
한 가지 이점은 존재에 대한 검사가 적절하게 추상적이라는 것이다 : (userExists가) 가까운 문제 도메인에, 약간 더 읽을 수, 이하 'computery'경우 : 경우 (사용자 == NULL)
timoxley

그리고 'if (x == null)'은 수십 년 전의 패턴이며, 이전에 보지 못했다면 매우 오랫동안 코드를 작성하지 않은 것입니다. 수백만 줄의 코드). "컴퓨터"? 우리는 데이터베이스 액세스에 대해 이야기하고 있습니다 ...
Lloyd Sargent

3

사용자를 찾을 수없는 경우가 자주 발생하고 상황에 따라 다양한 방법으로 처리하려는 경우 (때로는 예외를 던지거나 때로는 빈 사용자로 대체) F # Option또는 Haskell Maybe유형에 가까운 것을 사용할 수도 있습니다 '무조건 발견'과 '무가치'사례를 명시 적으로 분리합니다. 데이터베이스 액세스 코드는 다음과 같습니다.

public Option<UserEntity> GetUserById(Guid userId)
{
 //Imagine some code here to access database.....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return Option<UserEntity>.Nothing; 
 else
    return Option.Just(existingUserEntity);
}

그리고 이렇게 사용하십시오 :

Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
    // deal with it
} else {
    UserEntity value = result.GetValue();
}

불행히도, 모든 사람들은 이와 같은 유형을 굴리는 것 같습니다.


2

나는 일반적으로 null을 반환합니다. 그것은 예외를 던지거나 무언가를 시도해 보지 않고 무언가가 망가 졌는지 감지하는 빠르고 쉬운 메커니즘을 제공합니다.


2

컬렉션 유형의 경우 빈 컬렉션을 반환하고 다른 모든 유형의 경우 반환 유형과 동일한 인터페이스를 구현하는 객체를 반환하기 위해 NullObject 패턴을 사용하는 것이 좋습니다. 패턴 체크 아웃 링크 텍스트 에 대한 자세한 내용

NullObject 패턴을 사용하면 다음과 같습니다.

public UserEntity GetUserById(Guid userId)

{// 데이터베이스에 액세스하는 코드가 있다고 상상해보십시오 .....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();  
 else
    return existingUserEntity;

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 

2

다른 사람들이 말한 것을 더 간결하게 표현하려면 ...

예외적 인 상황은 예외입니다

이 방법이 순수한 데이터 액세스 계층이라면 select 문에 포함 된 일부 매개 변수가 주어지면 객체를 빌드 할 행을 찾을 수 없으므로 null을 반환하는 것이 허용됩니다. 데이터 액세스 로직입니다.

반면에 매개 변수가 기본 키를 반영 할 것으로 예상하고 하나 이상의 행을 가져와야하는 경우 하나 이상의 행을 가져 오면 예외가 발생합니다. 0은 null을 반환해도 좋고 2는 그렇지 않습니다.

이제 LDAP 공급자에 대해 확인한 로그인 코드가 있고 DB를 확인하여 자세한 내용을 확인하고 항상 동기화해야한다고 예상 한 경우 예외를 던질 수 있습니다. 다른 사람들이 말했듯이 비즈니스 규칙입니다.

이제 이것이 일반적인 규칙 이라고 말할 것입니다 . 그것을 깨고 싶을 때가 있습니다. 그러나 C # (많은)과 Java (그것의 약간)에 대한 나의 경험과 실험은 조건부 논리를 통해 예측 가능한 문제를 처리하는 것보다 예외를 처리 하는 것이 훨씬 비싸다 는 것을 가르쳐주었습니다 . 어떤 경우에는 2 ~ 3 배 정도 더 비싸게 조정하고 있습니다. 따라서 코드가 루프로 끝날 수 있다면 null을 반환하고 테스트하는 것이 좋습니다.


2

내 의사 PHP / 코드를 용서하십시오.

나는 그것이 의도 한 결과의 사용에 달려 있다고 생각합니다.

리턴 값을 편집 / 수정하고 저장하려면 빈 오브젝트를 리턴하십시오. 이렇게하면 동일한 기능을 사용하여 새 객체 나 기존 객체의 데이터를 채울 수 있습니다.

기본 키와 데이터 배열을 가져 와서 행을 데이터로 채우고 결과 레코드를 db에 저장하는 함수가 있다고 가정 해보십시오. 객체를 어느 쪽이든 데이터로 채우려 고하므로 게터에서 빈 객체를 다시 가져 오는 것이 큰 이점이 될 수 있습니다. 그렇게하면 어느 경우 든 동일한 작업을 수행 할 수 있습니다. 무엇이든 getter 함수의 결과를 사용합니다.

예:

function saveTheRow($prim_key, $data) {
    $row = getRowByPrimKey($prim_key);

    // Populate the data here

    $row->save();
}

여기서 동일한 일련의 작업이이 유형의 모든 레코드를 조작 함을 알 수 있습니다.

그러나 반환 값의 궁극적 인 의도가 데이터로 무언가를 읽고 수행하는 경우 null을 반환합니다. 이렇게하면 반환 된 데이터가 없는지 신속하게 확인할 수 있으며 사용자에게 적절한 메시지를 표시 할 수 있습니다.

일반적으로 함수에서 데이터를 검색하는 예외를 잡아서 (오류 메시지 등을 기록 할 수 있습니다 ...) 캐치에서 null을 직접 반환합니다. 일반적으로 최종 사용자에게는 문제가 무엇인지 중요하지 않으므로 데이터를 가져 오는 함수에 오류 로깅 / 처리를 직접 캡슐화하는 것이 가장 좋습니다. 대기업에서 공유 코드베이스를 유지 관리하는 경우 게으른 프로그래머라도 적절한 오류 로깅 / 처리를 강제 할 수 있기 때문에 특히 유용합니다.

예:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

이것이 나의 일반적인 규칙입니다. 지금까지 잘 작동했습니다.


2

흥미로운 질문과 나는 "올바른"답변이 없다고 생각합니다. 왜냐하면 항상 코드의 책임에 달려 있기 때문입니다. 발견 된 데이터가 문제가 없는지 방법이 알고 있습니까? 대부분의 경우 대답은 "아니오"이므로 null을 반환하고 호출자가 상황을 처리하도록하는 것이 완벽한 이유입니다.

던지는 메소드와 널 리턴 메소드를 구별하는 좋은 방법은 팀에서 규칙을 찾는 것입니다. null을 리턴 할 수있는 메소드의 이름은 "Find ..."로 다르게 지정할 수 있습니다.


+1 나는 그 함수가 어떻게 소비되는지 프로그래머에게 알리기 위해 균일 한 명명 규칙을 사용하는 아이디어를 좋아한다.
7wp

1
갑자기 나는이 LINQ가 무엇임을 인식 : 첫 번째 (...) 대 FirstOrDefault (...) 고려
마크 Wittke

2

반환 된 객체가 반복 될 수있는 경우 빈 객체를 반환하므로 null을 먼저 테스트 할 필요가 없습니다.

예:

bool IsAdministrator(User user)
{
    var groupsOfUser = GetGroupsOfUser(user);

    // This foreach would cause a run time exception if groupsOfUser is null.
    foreach (var groupOfUser in groupsOfUser) 
    {
        if (groupOfUser.Name == "Administrators")
        {
            return true;
        }
    }

    return false;
}

2

모든 메소드에서 null을 반환하지 않고 Option 함수 유형을 대신 사용하는 것이 좋습니다. 결과를 반환 할 수없는 메서드는 null이 아닌 빈 옵션을 반환합니다.

또한 결과를 반환 할 수없는 이러한 메서드는 이름을 통해이를 나타내야합니다. 나는 보통 메서드 이름의 시작 부분에 Try 또는 TryGet 또는 TryFind를 넣어 빈 결과를 반환 할 수 있음을 나타냅니다 (예 : TryFindCustomer, TryLoadFile 등).

이를 통해 호출자 는 결과에 수집 파이프 라이닝 (Martin Fowler의 수집 파이프 라인 참조)과 같은 다양한 기술을 적용 할 수 있습니다.

다음은 코드 복잡성을 줄이기 위해 null 대신 Option을 반환하는 또 다른 예입니다. Cyclomatic Complexity를 줄이는 방법 : Option Functional Type


1
나는 대답을 썼는데, 스크롤 할 때 당신과 비슷한 것을 볼 수 있으며 동의합니다 .0 또는 1 개의 요소가있는 일반 컬렉션으로 옵션 유형을 구현할 수 있습니다. 추가 링크에 감사드립니다.
Gabriel P.

1

갈기 더 많은 고기 : 일부 DAL이 조언 한대로 Get DersonByID에 대해 DAL이 NULL을 반환한다고 가정 해 봅시다. NULL을 받으면 (얇은) ​​BLL은 어떻게해야합니까? 그 NULL을 전달하고 최종 소비자가 그것에 대해 걱정하게하십시오 (이 경우 ASP.Net 페이지)? BLL이 예외를 던지는 것은 어떻습니까?

BLL은 ASP.Net 및 Win App 또는 다른 클래스 라이브러리에서 사용 중일 수 있습니다. 최종 소비자가 GetPersonByID 메서드가 null을 반환한다는 것을 본질적으로 "알고"(널 형식이 사용되지 않는 한) ).

내 가치가있는 것은 내 DAL이 아무것도 발견되지 않으면 NULL을 반환한다는 것입니다. 일부 개체의 경우 괜찮습니다. 0 : 다수의 목록 일 수 있으므로 아무 것도없는 것이 좋습니다 (예 : 즐겨 찾는 책 목록). 이 경우 내 BLL은 빈 목록을 반환합니다. 내가없는 경우 대부분의 단일 엔터티 항목 (예 : 사용자, 계정, 송장)의 경우 분명히 문제가되고 값 비싼 예외가 발생합니다. 그러나 응용 프로그램에서 이전에 제공 한 고유 식별자로 사용자를 검색하는 경우 항상 사용자를 반환해야합니다. 예외는 예외적으로 "적절한"예외입니다. BLL (ASP.Net, f'rinstance)의 최종 소비자는 상황이 거칠어지기를 기대하기 때문에 GetPersonByID에 대한 모든 단일 호출을 try-catch 블록으로 래핑하는 대신 처리되지 않은 예외 핸들러가 사용됩니다.

접근 방식에 눈부신 문제가있는 경우, 항상 배우고 싶어서 알려주십시오. 다른 포스터가 말했듯이, 예외는 비용이 많이 드는 일이며 "먼저 확인"접근 방식은 좋지만 예외는 예외입니다.

나는이 게시물을 즐기고 있는데, "의존하는"시나리오에 대한 좋은 제안이 많이 있습니다 :-)


그리고 물론 오늘은 BLL에서 NULL을 반환하는 시나리오를 보았습니다 .-) 즉, 여전히 예외를 throw하고 소비하는 클래스에서 try / catch를 사용할 수 있지만 여전히 문제가 있습니다. : 소비하는 클래스는 try / catch를 사용하는 방법을 어떻게 알 수 있습니까? NULL을 확인하는 방법과 비슷합니까?
Mike Kingscott

메소드가 @throws doctag를 통해 예외를 발생 시킨다는 것을 문서화 할 수 있으며 @return doctag에서 널을 리턴 할 수 있다는 사실을 문서화 할 수 있습니다.
timoxley 2009

1

코드베이스의 건강을 위해 함수가 null을 반환해서는 안된다고 생각합니다. 몇 가지 이유를 생각할 수 있습니다.

널 (null) 참조를 처리하는 많은 가드 절이 if (f() != null)있습니다.

null허용되는 답변 입니까 , 아니면 문제입니까? 특정 객체에 대해 null이 유효한 상태입니까? (당신이 코드의 클라이언트라고 상상해보십시오). 모든 참조 유형이 null 일 수는 있지만 그래야합니까?

null코드 기반의 성장에 따라 매달려 주위에 거의 항상 수시로 몇 가지 예상치 못한 NullRef 예외를 제공 할 것입니다.

함수형 프로그래밍을 tester-doer pattern구현하거나 구현하는 솔루션이 있습니다 option type.


0

나는 "IsItThere ()"메소드와 "GetItForMe ()"메소드라는 두 가지 메소드가 필요하다는 대답의 수 (웹 전체)에 당황하여 경쟁 조건을 초래합니다. 한 번의 테스트에서 null을 반환하고 변수에 변수를 할당하고 Null에 대한 변수를 확인하는 함수에 어떤 문제가 있습니까? 내 이전 C 코드는

if (NULL! = (변수 = 함수 (인수 ...))) {

따라서 변수의 값 (또는 null)과 결과를 한 번에 얻습니다. 이 관용구를 잊어 버렸습니까? 왜?


0

나는 여기로 향하는 대부분의 게시물에 동의합니다 null.

내 추론은 nullable이 아닌 속성으로 빈 객체를 생성하면 버그가 발생할 수 있다는 것입니다. 예를 들어 int ID속성이 있는 엔터티 의 초기 값은 ID = 0완전히 유효한 값입니다. 어떤 상황에서 해당 객체가 데이터베이스에 저장되면 나쁜 것입니다.

반복자가있는 경우 항상 빈 컬렉션을 사용합니다. 같은 것

foreach (var eachValue in collection ?? new List<Type>(0))

내 의견으로는 코드 냄새입니다. 컬렉션 속성은 null이되어서는 안됩니다.

가장자리 케이스는 String입니다. 많은 사람들 String.IsNullOrEmpty이 실제로 필요하지는 않지만 항상 빈 문자열과 null을 구분할 수는 없다고 말합니다 . 또한 일부 데이터베이스 시스템 (Oracle)은 이들을 전혀 구별하지 않기 때문에 ( ''로 저장 됨 DBNULL) 동일하게 처리해야합니다. 어느 텍스트 상자도 대부분 교환 형식에 대해 서로 다른 표현을하면서 그 이유는 대부분의 문자열 값 중 하나를, 사용자 입력이나 외부 시스템에서 와서 ''null. 따라서 사용자가 값을 제거하려는 경우에도 입력 제어를 지우는 것 이상을 수행 할 수 없습니다. 또한 nvarcharDBMS가 oracle이 아닌 경우 널 입력 가능 및 널 입력 불가능 데이터베이스 필드 의 구별은 의문의 여지가 있습니다.''이상하지 않습니다. UI에서이를 허용하지 않으므로 제약 조건이 매핑되지 않습니다. 제 생각에는 여기에서 항상 똑같이 처리하십시오.

예외 및 성능에 관한 질문과 관련하여 : 프로그램 논리에서 완전히 처리 할 수없는 예외를 처리하는 경우 프로그램이 수행하는 모든 작업을 중단하고 사용자가 방금 수행 한 작업을 다시 수행하도록 요청해야합니다. 이 경우 a의 성능 저하 catch는 실제로 가장 걱정할 사항이 아닙니다. 사용자에게 방에 코끼리가 있는지 물어봐야합니다 (전체 UI를 다시 렌더링하거나 인터넷을 통해 HTML을 보내는 것을 의미 함). 따라서 " 예외가있는 프로그램 흐름 "의 안티 패턴을 따르지 않는다면 귀찮게하지 마십시오. "Validation Exception"과 같은 경계선 경우에도 사용자에게 다시 요청해야하기 때문에 성능은 실제로 문제가되지 않습니다.


0

비동기 TryGet 패턴 :

동기 방법, 나는 생각 @Johann 게렐의 답변 입니다 모든 경우에 사용하는 패턴.

그러나 out매개 변수가 있는 TryGet 패턴 은 비동기 메서드에서 작동하지 않습니다.

C # 7의 튜플 리터럴을 사용하면 다음과 같이 할 수 있습니다.

async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
    if (InternalIdExists(id))
    {
        o = await InternalGetSomeObjectAsync(id);

        return (true, o);
    }
    else
    {
        return (false, default(SomeObject));
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.