어떻게:단락 역 삼항 연산자가 구현되었습니다.씨#?그게 그렇게 중요한 건가?
-
22-07-2019 - |
문제
객체에 대한 할당을 선택하기 위해 삼항 연산자, null 병합 연산자 또는 중첩된 if-else 문을 사용한다고 가정합니다.이제 조건문 내에서 결과를 임시 변수에 입력하고 해당 상태를 캡처하여 비교한 다음 잠재적으로 할당해야 하는 비용이 많이 들고 일시적인 작업을 평가한다고 가정해 보겠습니다.
C#과 같은 언어에서는 이 경우를 처리하기 위해 새로운 논리 연산자를 어떻게 구현합니까?그래야 할까요?C#에서 이 경우를 처리할 수 있는 기존 방법이 있습니까?다른 언어?
예를 들어 직접 비교를 찾고 있다고 가정할 때 삼항 또는 널 병합 연산자의 장황함을 줄이는 일부 사례가 극복되었습니다.보다 Null Coalescing 연산자를 사용하는 독특한 방법, 특히 지원을 위해 연산자의 사용을 확장할 수 있는 방법에 대한 논의 String.IsNullOrEmpty(string)
.방법 참고 존 스키트 을 사용하고 있습니다 PartialComparer
~에서 MiscUtil
, 다시 포맷하려면 0
~에게 null
에스,
이것이 왜 필요한가요?자, 지름길 없이 복잡한 객체에 대한 비교 방법을 작성하는 방법을 살펴보십시오(인용된 토론의 예):
public static int Compare( Person p1, Person p2 )
{
return ( (result = Compare( p1.Age, p2.Age )) != 0 ) ? result
: ( (result = Compare( p1.Name, p2.Name )) != 0 ) ? result
: Compare( p1.Salary, p2.Salary );
}
Jon Skeet은 평등 사례를 대체하기 위해 새로운 비교를 작성합니다.이를 통해 null을 반환하는 새로운 특정 메서드를 작성하여 표현식을 확장할 수 있으므로 null 병합 연산자를 사용할 수 있습니다.
return PartialComparer.Compare(p1.Age, p2.Age)
?? PartialComparer.Compare(p1.Name, p2.Name)
?? PartialComparer.Compare(p1.Salary, p2.Salary)
?? 0;
Null 병합 연산자는 3개의 변이 아닌 2개의 변을 갖기 때문에 더 읽기 쉽습니다.부울 조건 절은 메소드로 분리되어 있습니다. 이 경우에는 다음을 반환합니다. null
표현식이 계속되어야 하는 경우.
조건을 더 쉽게 인라인으로 넣을 수 있다면 위의 표현식은 어떤 모습일까요?에서 표현을 가져오세요. PartialComparer.Compare
이는 반환 null
, 암시적 임시 변수와 함께 왼쪽 표현식의 평가를 사용할 수 있는 새로운 삼항 표현식에 배치합니다. value
:
return Compare( p1.Age, p2.Age ) unless value == 0
: Compare( p1.Name, p2.Name ) unless value == 0
: Compare( p1.Salary, p2.Salary );
표현식의 기본 "흐름"은 다음과 같습니다.
표현 ㅏ ~하지 않는 한 부울 비 어떤 경우에는 표현 씨
과부하된 비교 연산자라기보다는 단락 회로에 가깝다고 생각합니다. 역삼항 연산자.
- 이런 유형의 논리가 유용할까요?현재 Null 병합은 조건식을 사용하여 이를 수행하는 방법을 제공합니다.
(value == null)
. - 어떤 다른 표현에 대해 테스트하고 싶나요?우리는 들어 본 적이 있습니다
(String.IsNullOrEmpty(value))
. - 이를 연산자, 키워드 측면에서 언어로 표현하는 가장 좋은 방법은 무엇일까요?
해결책
개인적으로 나는 연산자의 단락을 피하고 메서드를 연결하도록 허용합니다.
public static int CompareChain<T>(this int previous, T a, T b)
{
if (previous != 0)
return previous;
return Comparer<T>.Default.Compare(a,b);
}
다음과 같이 사용하세요:
int a = 0, b = 2;
string x = "foo", y = "bar";
return a.Compare(b).CompareChain(x,y);
JIT에 의해 인라인될 수 있으므로 더 복잡해지지 않고 언어에 내장된 단락 기능을 수행할 수 있습니다.
위의 '구조'가 단순한 비교 이상에 적용될 수 있는지 묻는 질문에 대한 응답으로, 계속할지 여부를 사용자가 명시적이고 제어할 수 있도록 선택함으로써 가능합니다.이는 본질적으로 더 복잡하지만 작업이 더 유연하므로 이는 불가피합니다.
public static T ElseIf<T>(
this T previous,
Func<T,bool> isOK
Func<T> candidate)
{
if (previous != null && isOK(previous))
return previous;
return candidate();
}
그런 다음 그렇게 사용하십시오
Connection bestConnection = server1.GetConnection()
.ElseIf(IsOk, server2.GetConnection)
.ElseIf(IsOk, server3.GetConnection)
.ElseIf(IsOk, () => null);
이는 다음을 변경할 수 있다는 점에서 최대의 유연성을 제공합니다. 괜찮아요 어떤 단계에서든 확인하고 완전히 게으르다.OK 확인이 모든 경우에 동일한 상황에서는 이렇게 단순화하고 확장 방법을 완전히 피할 수 있습니다.
public static T ElseIf<T>(
Func<T,bool> isOK
IEnumerable<Func<T>[] candidates)
{
foreach (var candidate in candidates)
{
var t = candidate();
if (isOK(t))
return t;
}
throw new ArgumentException("none were acceptable");
}
linq로 이 작업을 수행할 수 있지만 이 방법은 좋은 오류 메시지를 제공하고 이를 허용합니다.
public static T ElseIf<T>(
Func<T,bool> isOK
params Func<T>[] candidates)
{
return ElseIf<T>(isOK, (IEnumerable<Func<T>>)candidates);
}
다음과 같이 읽기 좋은 코드로 이어지는 스타일:
var bestConnection = ElseIf(IsOk,
server1.GetConnection,
server2.GetConnection,
server3.GetConnection);
기본값을 허용하려면 다음을 수행하십시오.
public static T ElseIfOrDefault<T>(
Func<T,bool> isOK
IEnumerable<Func<T>>[] candidates)
{
foreach (var candidate in candidates)
{
var t = candidate();
if (isOK(t))
return t;
}
return default(T);
}
분명히 위의 모든 내용은 람다를 사용하여 매우 쉽게 작성할 수 있으므로 구체적인 예는 다음과 같습니다.
var bestConnection = ElseIfOrDefault(
c => c != null && !(c.IsBusy || c.IsFull),
server1.GetConnection,
server2.GetConnection,
server3.GetConnection);
다른 팁
당신은 이미이 질문에 대한 좋은 답변을 많이 받았으며, 나는이 특정 파티에 늦었습니다. 그러나 귀하의 제안은 C#이 가졌던 더 일반적으로 유용한 작업의 특별한 경우, 즉 표현 맥락에서 임시 계산에 이름을 제시하는 능력의 특별한 경우라는 점에 주목할 가치가 있다고 생각합니다.
실제로 C#에는이 연산자가 있지만 쿼리 이해 만하면됩니다. C# 3의 연산자로 이것을 추가 할 수 있었으면 좋겠다.
public static int Compare(Person p1, Person p2) =>
let ages = Compare(p1.Age, p2.Age) in
ages != 0 ?
ages :
let names = Compare(p1.Name, p2.Name) in
names != 0 ?
names :
Compare(p1.Salary, p2.Salary);
"표현"은 너무 유용합니다, 그리고 발견 된 너무 적은 언어, 그리고 나는 언어 디자이너가 왜 버전 1에 즉시 추가하지 않는지 진정으로 이해하지 못합니다.
C# 이이 기능을 가지고 있다면 제안이 있습니다.
A() unless B() : C()
간단합니다
let a = A() in B() ? C() : a
이해하기 어려운 것은 거의없고 보너스를 사용합니다. a
표현으로 B()
그리고 C()
당신이 좋아한다면.
람다가있는 모든 언어로 표현을 모방 할 수 있습니다. 물론이야 let x = y in z
간단합니다 (x=>z)(y)
, 그러나 C#은 모든 람다에서 대의원 유형으로 변환해야하기 때문에 C#에이를 작성하는 간결한 방법은 없습니다.
또한 Roslyn에서는 우리가 할 수는 있지만 임시를 맹세로 대표하지 않습니다. 오히려, 우리는 그 아래의 한 수준까지 가고 "값을 생성 할 수있는 일련의 연산, 그 중 하나는이 표현의 가치가 될 것"에 대한 표현을 가지고 있습니다. "x = y in z"는 단순히 서열 "x, x = y, z, dallelocate x"입니다. 여기서 세 번째 요소는 값입니다. 그리고 원래의 Pre-Roslyn C# 컴파일러에서 우리는 내부 연산자 "왼쪽"및 "오른쪽"이 있었는데, 이는 두 개의 표현식을 취하고 왼쪽 또는 오른쪽을 생산하는 이진 연산자 였으므로 생성 할 수있었습니다. ((allocate x) right ((x = y) right z)) left (deallocate x)
.
내 요점은 : 우리는 종종 비정상적인 구두점으로 맞춤형 언어 기능에 대한 요청을 받지만 일반적으로 구현하는 것이 더 좋았을 것입니다. 기본 빌딩 블록 이 운영자를 자연스럽게 구축 할 수 있습니다.
제안 된 하나의 구현을 매우 장황한 질문에서 벗어나려면 unless
예어.
(표현 ㅏ) unless
(부울 비)u003Cmagical "in which case" operator> (표현 씨)
... 모든 것이있을 것입니다.
부울 표현 비 표현의 평가에 접근 할 수 있습니다 ㅏ 키워드를 통해 value
. 표현 씨 가질 수 있습니다 unless
표현식의 키워드, 단순하고 선형 체인을 허용합니다.
후보자u003Cmagical "in which case" operator> :
:
|
?:
otherwise
예어
모든 기호의 사용은 평균 개발자의 가독성을 감소시키는 경향이 있습니다. 심지어 ??
연산자는 널리 사용되지 않습니다. 나 자신은 장황 코드를 개발하는 것을 선호하지만 지금부터 1 년 동안 쉽게 읽을 수 있습니다.
그래서 당신의 후보자 :
표현 a 부울 B이 경우 표현 씨.
할 것입니다
표현 a 부울 B 소튼 표현 씨.
나와 같은 많은 사람들이 여전히 사용할 것입니다.
if (B) {expression C;}
else {expression A;}
이것은 당신이 큰 팀, 다른 배경, 각각의 팀 마스터의 한 언어 및 다른 사람들의 사용자가있는 소프트웨어를 개발할 때 발생합니다.
더 @Shuggycouk: 아, 이것이 단순한 비교 이상의 경우에도 효과가있을 수 있습니까? 나는 c# 3과 확장 방법을 사용하지 않았지만 이전의 예에 대해서는 아래의 A를 선언 할 수 있다고 생각합니다.
public delegate bool Validation<T>( T toTest );
public static T Validate<T>( this T leftside, Validation<T> validator )
{
return validator(leftside) ? leftside : null;
}
그 뒤에, Skeet 당 :
Validation<Connection> v = ( Connection c ) => ( c != null && !( c.IsBusy || c. IsFull ) );
Connection bestConnection =
server1.GetConnection().Validate( v ) ??
server2.GetConnection().Validate( v ) ??
server3.GetConnection().Validate( v ) ?? null;
이것이 C#에서 어떻게 작동 하는가? 의견은 감사합니다. 고맙습니다.
응답으로 Shuggycouk:
그렇다면 이것은 C# 3의 확장 방법입니까? 또한, 여기서 결과는 임의의 표현이 아니라 int입니다. 또 다른 비교 방법을 과부하시키는 데 유용합니다. 최상의 연결을 선택하기위한 표현을 원한다고 가정 해 봅시다. 이상적으로는 다음을 단순화하기를 원합니다.
Connection temp;
Connection bestConnection =
( temp = server1.GetConnection() ) != null && !(temp.IsBusy || temp.IsFull) ? temp
: ( temp = server2.GetConnection() ) != null && !(temp.IsBusy || temp.IsFull ) ? temp
: ( temp = server3.GetConnection() ) != null && !(temp.IsBusy || temp.IsFull ) ? temp
: null;
좋아, 그래서 하나는 방법을 가질 수있다
bool IsOk( Connection c )
{
return ( c != null && !(c.IsBusy || c.IsFull) );
}
생산할 것 :
Connection temp;
Connection bestConnection =
( temp = server1.GetConnection() ) && IsOk( temp ) ? temp
: ( temp = server2.GetConnection() ) && IsOk( temp ) ? temp
: ( temp = server3.GetConnection() ) && IsOk( temp ) ? temp
: null;
그러나 여기서 비교를위한 방법 체인은 어떻게 작동할까요? 나는 모양을 숙고하고 있습니다.
Connection bestConnection =
server1.GetConnection() unless !IsOk(value) otherwise
server2.GetConnection() unless !IsOk(value) otherwise
server3.GetConnection() unless !IsOk(value) otherwise null;
조건부의 결과가 원래 조건부에 있던 방법의 표현 또는 결과가되기를 원한다면 지금까지의 후프가 있다고 생각합니다.
나는 그러한 방법으로 반환 된 물체가 생산 비용이 많이 들거나 다음에 메소드가 호출 될 때 변경 될 것이라고 가정합니다.