읽기 전용 필드에 대해 불순한 메소드가 호출 됨


83

내가 사용하고 비주얼 스튜디오 2010 + ReSharper에서를 하고 다음과 같은 코드에 경고를 표시합니다 :

if (rect.Contains(point))
{
    ...
}

rectreadonly Rectangle필드이고 Resharper는 다음 경고를 표시합니다.

"값 유형의 읽기 전용 필드에 대해 임 퓨어 메소드가 호출되었습니다."

불순한 방법은 무엇이며이 경고가 표시되는 이유는 무엇입니까?


답변:


96

먼저 Jon, Michael 및 Jared의 답변은 본질적으로 정확하지만 추가하고 싶은 몇 가지가 더 있습니다.

"불순한"방법이란 무엇을 의미합니까?

순수한 방법을 특성화하는 것이 더 쉽습니다. "순수한"방법에는 다음과 같은 특성이 있습니다.

  • 출력은 전적으로 입력에 의해 결정됩니다. 그 출력은 시간이나 하드 디스크의 비트와 같은 외부성에 의존하지 않습니다. 그 출력은 그 역사에 의존하지 않습니다. 주어진 인수로 메서드를 두 번 호출하면 동일한 결과를 얻을 수 있습니다.
  • 순수한 방법은 주변 세계에서 관찰 가능한 돌연변이를 생성하지 않습니다. 순수 메소드는 효율성을 위해 개인 상태를 변경하도록 선택할 수 있지만 순수 메소드는 인수 필드를 변경하지 않습니다.

예를 들어, Math.Cos순수한 방법입니다. 출력은 입력에만 의존하며 입력은 호출에 의해 변경되지 않습니다.

불순한 방법은 순수하지 않은 방법입니다.

읽기 전용 구조체를 불순한 메서드로 전달하는 위험은 무엇입니까?

떠오르는 두 가지가 있습니다. 첫 번째는 Jon, Michael 및 Jared가 지적한 것이며 Resharper가 경고하는 것입니다. 구조체에서 메서드를 호출 할 때 메서드가 변수를 변경하려는 경우 항상 수신자 인 변수에 대한 참조를 전달합니다.

그렇다면 변수가 아닌 값에 대해 이러한 메서드를 호출하면 어떻게 될까요? 이 경우 임시 변수를 만들고 값을 복사 한 다음 변수에 대한 참조를 전달합니다.

읽기 전용 변수는 생성자 외부에서 변경할 수 없기 때문에 값으로 간주됩니다. 따라서 우리는 변수를 다른 변수에 복사하고 있으며, 불순한 방법은 변수를 변경하려는 경우 복사본을 변경하는 것입니다.

이것이 readonly 구조체를 수신자 로 전달할 위험입니다 . 읽기 전용 필드를 포함하는 구조체를 전달할 위험도 있습니다. 읽기 전용 필드를 포함하는 구조체는 일반적인 관행이지만 기본적으로 유형 시스템에 현금화 할 자금이 없다는 수표를 작성합니다. 특정 변수의 "읽기 전용"은 저장소 소유자가 결정합니다. 참조 유형의 인스턴스는 자체 저장소를 "소유"하지만 값 유형의 인스턴스는 그렇지 않습니다!

struct S
{
  private readonly int x;
  public S(int x) { this.x = x; }
  public void Badness(ref S s)
  {
    Console.WriteLine(this.x);   
    s = new S(this.x + 1);
    // This should be the same, right?
    Console.WriteLine(this.x);   
  }
}

this.xx는 읽기 전용 필드이고 Badness생성자가 아니기 때문에 변경되지 않을 것이라고 생각합니다 . 그러나...

S s = new S(1);
s.Badness(ref s);

... 그것의 허위성을 분명히 보여줍니다. thiss같은 변수를 참조하고, 변수는 읽기 전용되지 않습니다!


충분히 공평 하지만 다음 코드를 고려하십시오. ToIntstruct Id { private readonly int _id; public Id(int id) { _id = id; } public int ToInt() => _id; } 가 왜 불순한가요?
boskicthebrain

@boskicthebrain : 귀하의 질문이 실제로 "Resharper가 이것이 불순하다고 생각하는 이유는 무엇입니까?"입니까? 그것이 당신의 질문이라면 R #에서 일하는 사람을 찾아서 물어보십시오!
Eric Lippert

3
Resharper는 메서드가 무효이고 return. 이를 바탕으로 유일한 기준은 메서드에 [Pure]속성 이 있는지 여부 입니다.
bornfromanegg dec.

나는 "우리는 항상 수신자 인 변수에 대한 참조를 전달한다"라는이 문장이 나에게 약간 혼란 스럽다는 것을 발견했다. PO의 경우 '변수'는 ​​무엇을 의미합니까? 나는 그것이 rect. 의 복사본이 메서드에 rect전달된다는 Contains말입니까?
xtu

51

불순한 방법은 그 가치를 그대로 남겨 두는 것이 보장되지 않는 방법입니다.

.NET 4에서는 메서드와 형식을 데코레이션 [Pure]하여 순수로 선언 할 수 있으며 R #은이를 인식합니다. 불행히도 다른 사람의 구성원에게 적용 할 수 없으며 내가 아는 한 .NET 3.5 프로젝트에서 유형 / 구성원이 순수하다고 R #을 설득 할 수 없습니다. (이것은 노다 타임에서 항상 나를 물고 있습니다.)

아이디어는 당신이 변수에있는 변이를 메서드를 호출하고 있지만 읽기 전용 필드를 호출하는 경우, 아마 것입니다 하지 R 번호는 이것에 대해 경고합니다, 그래서 당신이 원하는 일을. 예를 들면 :

public struct Nasty
{
    public int value;

    public void SetValue()
    {
        value = 10;
    }
}

class Test
{
    static readonly Nasty first;
    static Nasty second;

    static void Main()
    {
        first.SetValue();
        second.SetValue();
        Console.WriteLine(first.value);  // 0
        Console.WriteLine(second.value); // 10
    }
}

실제로 순수한 모든 메서드가 그렇게 선언 된 경우 정말 유용한 경고가 될 것입니다. 불행히도 그들은 그렇지 않으므로 많은 오 탐지가 있습니다 :(


그러면 불순한 메서드가 전달 된 가변 값 유형의 기본 필드를 변경할 수 있다는 의미입니까?
산성

@Acidic : 인수 값이 아니라 불순한 메서드라도 그렇게 할 수 있지만 당신 이 그것을 부르는입니다 . (방법도 매개 변수가없는 경우, 내 예를 참조하십시오.)
존 소총을

2
JetBrains.Annotations.PureAttribute대신 사용할 수 있으며 System.Diagnostics.Contracts.PureAttributeReSharper 코드 분석에 대해 동일한 의미를 가지며 .NET 3.5, .NET 4 또는 Silverlight에서 동일하게 작동해야합니다. XML 파일을 사용하여 소유하지 않은 어셈블리에 외부 주석을 달 수도 있습니다 (ReSharper bin 경로의 ExternalAnnotations 디렉토리를 확인하십시오). 정말 유용 할 수 있습니다!
Julien Lebosquain

5
@JulienLebosquain : 특히 오픈 소스 프로젝트의 경우 도구 별 주석 추가를 시작하는 것을 정말 꺼려합니다. 좋은이 ... 옵션으로 알고 있지만합니다
존 소총을

1
실제로 나는 System.Diagnostics.Contracts.PureAttributeR # 8.2에서이 경고를 억제하지 않는 것을 발견했습니다 JetBrains.Annotations.PureAttribute. 두 속성은 또한 다른 설명을 가지고 있습니다. contracts Pure속성은 "결과는 매개 변수에만 의존 함"을 의미하는 반면, JetBrains Pure는 결과를 계산하는 데 사용되는 객체 상태를 제외하지 않고 "표시 상태 변경을 일으키지 않음"을 의미합니다. (하지만 여전히 계약 Pure이 경고에 같은 효과를 가지고 있지 아마 버그입니다.)
Wormbo

15

짧은 대답은 이것이 잘못된 긍정이며 경고를 무시해도 안전하다는 것입니다.

더 긴 대답은 읽기 전용 값 형식에 액세스하면 복사본 이 만들어 지므로 메서드에 의한 값 변경은 복사본에만 영향을줍니다. ReSharper Contains는 이것이 순수한 방법 이라는 것을 인식하지 못합니다 (부작용이 없음을 의미 함). Eric Lippert가 여기에 대해 설명합니다. Readonly Structs 변형


2
완전히 이해 될 때까지이 경고를 무시하지 마십시오 !!! 이것이 안전 할 수있는 한 가지 좋은 예는 다음 private readonly SpinLock _spinLock = new SpinLock();과 같습니다 .-이러한 잠금은 완전히 쓸모가 없습니다 (읽기 전용 수정자는 Enter 메서드가 호출 될 때마다 즉석 복사가 생성되기 때문에)
Jan

11

Reshaprer가 메서드 Containsrect값을 변경할 수 있다고 믿는 것처럼 들립니다 . 때문에 rect, A는 readonly structC # 컴파일러 값 사본은 운전자로부터 돌연변이 방법을 방지 할 수 readonly필드. 기본적으로 최종 코드는 다음과 같습니다.

Rectangle temp = rect;
if (temp.Contains(point)) {
  ...
}

Resharper는 일시적으로 발생했기 때문에 즉시 손실되는 방식으로 Contains돌연변이 가 발생할 수 있음을 경고합니다 rect.


따라서 메서드에서 수행되는 논리에 영향을주지 않고 호출 된 값이 변경되는 것을 방지 할뿐입니다.
산성

5

불순한 방법은 부작용이있을 수있는 방법입니다. 이 경우 Resharper는 변경 될 수 있다고 생각하는 것 같습니다 rect. 아마 그렇지는 않지만 증거의 사슬이 끊어졌습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.