방프리

21.09.19 Chapter4. LINQ 활용 (Item 44) 본문

C#/Effective C#

21.09.19 Chapter4. LINQ 활용 (Item 44)

방프리 2021. 9. 19. 11:42

Item 44 : 바인딩된 변수는 수정하지 말라

 

이번에도 클로저를 사용했을 때의 경우 유의해야하는 부분이다. 

쿼리문을 실행하면서 지역 변수라든지 이미 클로저에서 캡쳐된 변수를 수정하는 코드를 넣을 수도 있는데

이는 컴파일러가 어떻게 동작을 할 것인지 결정에 따라 동작 방식이 크게 차이가 난다.

public class ModFilter
{
    private readonly int modulus;
    
    public ModFilter(int mod)
    {
        modulus = mod;
    }
    
    public IEnumerable<int> FindValues(IEnumerable<int> sequence)
    {
        int numValues = 0;
        return from n in sequence
            where n % modulus == 0
            select n * n / ++numValues;
    }
}

위의 코드를 컴파일러는 다음과 비슷한 코드로 동작한다.

public class ModFilter
{
    private sealed class Closure
    {
        public ModFilter outer;
        public int numValues;
        
        public int SelectClause(int n) => ((n * n) / ++this.numValues);
    }
    
    private readonly int modulus;
    
    public ModFilter(int mod)
    {
        this.modulus = mod;
    }
    
    private bool WhereClause(int n) => ((n % this.modulus) == 0);
    
    public IEnumerable<int> FindValues(IEnumerable<int> sequence)
    {
        var c = new Closure();
        c.outer = this;
        c.numValues = 0;
        
        return sequence.Where<int>(
            new Func<int, bool>(this.WhereClause))
            .Select<int, int>(
                new Func<int, int>(c.SelectClause));
    }
}

하단의 코드를 보면 컴파일러가 람다 표현식 내에서 사용하는 모든 지역변수를 포함하는 중첩 클래스를 

생성한다는 것을 알 수 있다.

실제로 람다 표현식 내에서 사용된 모든 지역변수들은 중첩 클래스 내의 필드로 대체된다.

여러 개의 쿼리를 연달아 수행할 때 바인딩된 변수의 값을 수정하면 앞서 말한 것처럼 컴파일러의 동작 방식에 따라 

문제가 발생될 수도 있다. 그러기에 바인딩된 변수는 수정하지 않는 것이 좋다.

Comments