명시 적 캐스팅 연산자를 사용하는 것이 합리적입니까 아니면 나쁜 해킹입니까?


24

나는 큰 물건을 가지고있다 :

class BigObject{
    public int Id {get;set;}
    public string FieldA {get;set;}
    // ...
    public string FieldZ {get;set;}
}

특수한 DTO와 유사한 객체 :

class SmallObject{
    public int Id {get;set;}
    public EnumType Type {get;set;}
    public string FieldC {get;set;}
    public string FieldN {get;set;}
}

개인적으로 BigObject를 SmallObject로 명시 적으로 캐스팅하는 개념을 발견했습니다. 단방향의 데이터 손실 작업이라는 것을 알고 있습니다. 매우 직관적이고 읽기 쉽습니다.

var small = (SmallObject) bigOne;
passSmallObjectToSomeone(small);

명시 적 연산자를 사용하여 구현됩니다.

public static explicit operator SmallObject(BigObject big){
    return new SmallObject{
        Id = big.Id,
        FieldC = big.FieldC,
        FieldN = big.FieldN,
        EnumType = MyEnum.BigObjectSpecific
    };
}

이제 메소드를 사용하여 SmallObjectFactory클래스를 만들 수 있습니다. FromBigObject(BigObject big)동일한 작업을 수행하고 종속성 주입에 추가하고 필요할 때 호출합니다 ...하지만 나에게는 훨씬 복잡하고 불필요합니다.

추신 : 나는 이것이 관련이 있는지 확실하지 않지만 ,로 설정하여 다른 설정 OtherBigObject으로 변환 할 수있을 것 입니다.SmallObjectEnumType


4
왜 생성자가 아닌가?
edc65

2
아니면 정적 팩토리 방법?
브라이언 고든

팩토리 클래스 또는 종속성 주입이 필요한 이유는 무엇입니까? 당신은 이분법을 허위로 만들었습니다.
user253751

1
@immibis-@Telastyn이 제안한 것에 대해 어떻게 든 생각하지 않았기 때문에 : .ToSmallObject()method (또는 GetSmallObject()). 이유의 순간적인 소멸-나는 내 생각에 뭔가 문제가 있다는 것을 알고 있었다. 그래서 나는 너희에게 물었다. :)
Gerino

3
이것은 ISmallObject 인터페이스의 완벽한 유스 케이스처럼 들립니다. ISmallObject 인터페이스는 BigObject에 의해서만 광범위한 데이터 / 동작의 제한된 세트에 대한 액세스를 제공하기위한 수단으로 만 구현됩니다. 특히 @Telastyn의 ToSmallObject방법 에 대한 아이디어와 결합 될 때 .
Marjan Venema

답변:


0

저의 겸손한 의견으로는 다른 답변들 중 어느 것도 옳지 않습니다. 이 스택 오버 플로우 질문 에서 가장 투표가 많은 답변은 매핑 코드가 도메인 외부에 있어야한다고 주장합니다. 귀하의 질문에 대답하기 위해, 아니오-캐스트 연산자의 사용법은 좋지 않습니다. DTO와 도메인 개체 사이에있는 매핑 서비스를 만들거나 automapper를 사용할 수 있습니다.


이것은 훌륭한 아이디어입니다. 이미 Automapper가 설치되어 있으므로 바람이 불 것입니다. 내가 가진 문제 만 : BigObject와 SmallObject가 어떻게 든 관련이 있는지 추적해야하지 않습니까?
Gerino

1
아니요. 매핑 서비스 이외의 다른 BigObject와 SmallObject를 함께 결합하면 이점이 없습니다.
Esben Skov Pedersen

7
정말? 오토 매퍼는 설계 문제에 대한 솔루션입니까?
Telastyn

1
BigObject는 SmallObject에 맵핑 가능하며, 고전적인 OOP 의미에서 서로 관련이 없으며 코드는이를 반영합니다 (두 오브젝트 모두 도메인에 존재하며 맵핑 기능은 다른 여러 구성 요소와 함께 맵핑 구성에 설정 됨). 모호한 코드 (불행한 연산자 재정의)를 제거하고 모델을 깨끗하게 유지합니다 (메서드가 없음). 그렇습니다. 솔루션 인 것 같습니다.
Gerino

2
@EsbenSkovPedersen이 솔루션은 불도저를 사용하여 사서함을 설치할 구멍을 파는 것과 같습니다. 운 좋게도 OP는 마당을 파고 싶었 으므로이 경우 불도저가 작동합니다. 그러나이 솔루션 은 일반적으로 권장하지 않습니다 .
Neil

81

그것은 ... 대단하지 않습니다. 나는이 영리한 트릭을 수행하는 코드로 작업했으며 혼란을 초래했습니다. 결국, 당신은 단지를 할당 할 수 있기를 기대 BigObjectSmallObject객체가 그들을 캐스팅 관련 정도 인 경우 변수입니다. 그래도 작동하지 않습니다. 유형 시스템에 관한 한 시도하지 않으면 컴파일러 오류가 발생하지만 관련이 없습니다. 주조 작업자가 새로운 물건을 만드는 것도 약간 불쾌합니다.

.ToSmallObject()대신 방법 을 권장합니다 . 실제로 무슨 일이 벌어지고 있는지 그리고 장황한 것에 대해 더 명확합니다.


18
Doh ... ToSmallObject ()가 가장 확실한 선택 인 것 같습니다. 때로는 가장 명백한 것이 가장 애매한;)
Gerino

6
mildly distasteful과소 평가입니다. 불행히도 언어는 이런 종류의 것을 유형 캐스트처럼 보이게합니다. 스스로 작성하지 않는 한 실제 객체 변환이라고 추측 할 수 없습니다. 1 인 팀에서 좋아요. 다른 사람과 공동 작업하는 경우 가장 좋은 경우에는 실제로 캐스트인지 아니면 미친 변형인지 파악해야하기 때문에 시간 낭비입니다.
Kent A.

3
@Telastyn 가장 악의적 인 코드 냄새가 아니라고 동의했습니다. 그러나 대부분의 프로그래머 가 동일한 객체 를 다른 유형으로 취급하도록 컴파일러에 지시하는 것으로 이해하는 작동시 새로운 객체의 숨겨진 생성은 코드를 처리 해야하는 사람에게는 불친절합니다. :)
Kent A.

4
일에 대한 .ToSmallObject(). 연산자를 재정의해서는 안됩니다.
이톨 레다 노

6
@dorus-적어도 .NET에서는 Get기존 항목을 반환 함을 의미합니다. 작은 객체에 대한 작업을 재정의하지 않는 한 두 번의 Get호출은 다른 객체를 반환하여 혼란 / 버그 / wtfs를 발생시킵니다.
Telastyn

11

내가 왜 필요한지 알 수 있지만 SmallObject문제에 다르게 접근합니다. 이 유형의 문제에 대한 나의 접근 방식은 Facade 를 사용하는 것 입니다. 유일한 목적은 캡슐화 BigObject하고 특정 멤버 만 사용할 수 있도록하는 것입니다. 이러한 방식으로, 사본이 아닌 동일한 인스턴스의 새로운 인터페이스입니다. 물론 당신은 할 수 있습니다 또한 사본을 수행 할,하지만 난 당신이 (예를 들어 외관과 함께 그 목적을 위해 생성하는 방법을 통해 그렇게하는 것이 좋습니다 것입니다 return new SmallObject(instance.Clone())).

Facade는 프로그램의 특정 섹션에서 파사드를 통해 사용할 수있는 멤버 만 사용할 수있게하여 알 수없는 내용을 효과적으로 사용할 수 없도록하는 여러 가지 장점이 있습니다. 또한 BigObject프로그램 전체에서 사용되는 방식에 대해 너무 걱정할 필요없이 향후 유지 관리에서 더 유연하게 변경할 수 있다는 엄청난 이점이 있습니다 . 구식 행동을 어떤 형태로 모방 할 수 있다면, SmallObject어디에서나 프로그램을 변경하지 않고도 이전과 동일한 방식으로 작업을 수행 할 수 있습니다 BigObject.

이것은 의미 BigObjectSmallObject아니라 다른 방법으로 의존 한다는 것을 명심하십시오 (내 겸손한 의견이어야 함).


파사드가 필드를 새로운 클래스에 복사하는 것보다 장점이 있다고 언급 한 유일한 장점은 복사를 피하는 것입니다 (사물에 필드가 부적절하지 않으면 문제가되지 않습니다). 반면에 정적 변환 방법과 달리 새 클래스로 변환해야 할 때마다 원래 클래스를 수정해야한다는 단점이 있습니다.
Doval

@Doval 나는 그것이 요점이라고 생각합니다. 새 클래스로 변환하지 않습니다. 필요한 경우 다른 파사드를 만듭니다. BigObject에 대한 변경 사항은 Facade 클래스에만 적용하면되며 사용되는 모든 곳이 아닙니다.
Neil

이 접근법과 Telastyn의 답변 사이의 흥미로운 차이점 중 하나 는 생성 SmallObject에 대한 책임이에 속하는지 SmallObject또는 BigObject입니다. 기본적으로이 방법은의 SmallObject개인 / 보호 멤버에 대한 종속성을 피하도록 BigObject합니다. 한 단계 더 나아가 확장 방법 SmallObject을 사용하여 개인 / 보호 된 구성원에 대한 종속성을 피할 수 있습니다 ToSmallObject.
Brian

@ 브라이언 당신은 BigObject그런 식으로 혼란 에 빠질 위험 이 있습니다. 비슷한 것을 원한다면 ? ToAnotherObject안에 확장 메소드 를 만들 것을 보증 할 것입니다 BigObject. BigObject아마도 이미 그 자체로 충분히 크기 때문에 이것들은 문제가되지 않아야합니다 . 또한 BigObject종속성 작성과 분리 할 수 있으므로 팩토리 등을 사용할 수 있습니다. 다른 방법 강하게 커플 BigObjectSmallObject. 이 특별한 경우에는 괜찮을지 모르지만 겸손한 견해로는 모범 사례가 아닙니다.
Neil

1
@Neil 실제로 Brian 잘못 설명했지만, 그는 옳습니다. 확장 방법은 커플 링을 제거합니다. 더 이상 BigObject연결 되지 않고 SmallObject인수를 가져 와서 BigObject리턴 하는 정적 메소드 일뿐 SmallObject입니다. 확장 메소드는 실제로 정적 메소드를 더 좋은 방법으로 호출하는 구문 설탕입니다. 확장 방법은없는 부분BigObject그것은 완전히 별도의 정적 방법이다. 실제로 확장 방법을 잘 사용하고 특히 DTO 변환에 매우 편리합니다.
Luaan

6

변경 가능한 참조 유형에 대한 캐스트는 ID 보존이라는 매우 강력한 규칙이 있습니다. 시스템은 일반적으로 소스 유형의 오브젝트가 대상 유형의 참조에 지정 될 수있는 상황에서 사용자 정의 된 캐스팅 연산자를 허용하지 않기 때문에 사용자 정의 된 캐스팅 조작이 변경 가능한 참조에 적합한 경우는 거의 없습니다 유형.

주어진 객체 가 두 캐스트 사이에서 수정 되었더라도 두 객체 모두 동일한 캐스트에 동일한 캐스트를 적용한 x=(SomeType)foo;후에 는 항상 그리고 영원히 참이어야한다는 요구 사항으로 제안 합니다. 이러한 상황은 예를 들어, 하나의 유형이 다른 유형의 오브젝트 쌍을 가지고 있고 각각이 다른 유형에 대해 불변의 참조를 보유하고 있고 다른 유형으로 오브젝트를 캐스트하면 해당 쌍의 인스턴스를 리턴하는 경우에 적용될 수 있습니다. 랩핑되는 오브젝트 의 ID 가 변경 불가능하고 동일한 유형의 두 랩퍼가 동일한 콜렉션을 랩핑하면 동일하다고보고하는 경우 가변 오브젝트에 대한 랩퍼 역할을하는 유형에도 적용 할 수 있습니다 .y=(SomeType)foo;x.Equals(y)

특정 예제는 변경 가능한 클래스를 사용하지만 어떤 형태의 정체성도 보존하지 않습니다. 따라서 캐스팅 연산자를 적절하게 사용하지 않는 것이 좋습니다.


1

괜찮을 수도 있습니다.

예제의 문제점은 그러한 예제 이름을 사용한다는 것입니다. 치다:

SomeMethod(long longNum)
{
  int num = (int)longNum;
  /* ... */

당신이 좋은 아이디어를했으면 이제, 어떤 길이INT 수단, 다음의 암시 적 캐스트 모두 intlong와에서 명시 적 캐스트 long로는 int확실히 이해할 수 있습니다. 방법 또한 이해할 3하게 3하고 직장에 또 다른 방법입니다 3. int.MaxValue + 1확인 된 컨텍스트에서 이것이 어떻게 실패하는지 이해할 수 있습니다. int.MaxValue + 1확인되지 않은 컨텍스트에서 결과 int.MinValue를 얻는 방법조차도 가장 어려운 것은 아닙니다.

마찬가지로 기본 유형 또는 암시 적 유형으로 암시 적으로 캐스트 할 때 상속이 발생하는 방식과 결과가 어떻게 될지 (또는 실패하는 방식)를 아는 사람은 이해할 수 있습니다.

이제 BigObjectSmallObject를 사용 하면이 관계가 어떻게 작동하는지 알 수 없습니다. 만약 당신의 실제 유형이 캐스팅 관계가 명백한 곳에있는 경우, 캐스팅은 실제로 많은 시간, 아마도 대부분의 경우이지만 실제로 좋은 생각 일 수 있습니다. 이것이 사실이라면 클래스 계층 구조에 반영되어야합니다. 일반적인 상속 기반 캐스팅으로 충분합니다.


사실 그들은 훨씬 더 질문에서 제공하는 것보다 아니에요 -하지만 예를 들어 BigObject을 설명 할 수 Employee {Name, Vacation Days, Bank details, Access to different building floors etc.}SmallObject수 있습니다 MoneyTransferRecepient {Name, Bank details}. 에서 Employee로 직접 번역 MoneyTransferRecepient하는 것이 필요하며 필요한 것보다 더 많은 데이터를 뱅킹 애플리케이션에 보낼 이유가 없습니다.
Gerino
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.