심판에 의해 전달 된 목록-이 동작을 설명하는 데 도움이


109

다음 프로그램을 살펴보십시오.

class Test
{
    List<int> myList = new List<int>();

    public void TestMethod()
    {
        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList(myList);

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList(List<int> myList)
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

나는 myList통과했을 것이라고 가정 ref했고 출력은

3
4

목록은 실제로 "ref에 의해 전달"되지만 sort함수 만 적용됩니다. 다음 문 myList = myList2;은 효과가 없습니다.

따라서 출력은 실제로 다음과 같습니다.

10
50
100

이 행동을 설명 할 수 있습니까? 실제로 참조에 의해 전달myList 되지 않은 경우 ( 효과가 적용되지 않는 것으로 보임 ) 어떻게 적용됩니까?myList = myList2myList.Sort()

나는 그 진술조차도 효과가없고 출력이 다음과 같을 것이라고 가정했습니다.

100
50
10

관찰 일뿐입니다 (문제가 여기에서 단순화되었음을 알고 있습니다).하지만 실제로 새 목록을 만드는 경우 ChangeLista가 List<int>아닌 a를 반환하는 것이 가장 좋은 것 같습니다 void.
Jeff B

답변:


110

목록에 대한 참조를 전달하고 있지만 참조로 목록 변수 전달 하지 않습니다. 따라서 변수 (즉, 참조- "포인터"라고 생각) 을 호출 할 때 복사되고 값이 변경됩니다 . 매개 변수를 내부 되지 않는 볼 .ChangeListChangeList TestMethod

시험:

private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);

그런 다음 에서 선언 된대로 지역 변수에 대한 참조를 전달 합니다 . 이제 내부 매개 변수를 재 할당하면 내부 변수도 재 할당하게 됩니다 .myRefTestMethodChangeList TestMethod


실제로 그렇게 할 수 있지만 정렬이 어떻게 적용되는지 알고 싶습니다.
nmdr

6
@Ngm은 - 당신이 호출 할 때 ChangeList, 단지 참조는 복사 - 동일한 개체입니다. 어떤 식 으로든 개체를 변경하면 해당 개체에 대한 참조가있는 모든 항목에 변경 사항이 적용됩니다.
Marc Gravell

225

처음에는 다음과 같이 그래픽으로 표시 할 수 있습니다.

초기화 상태

그런 다음 정렬이 적용됩니다. myList.Sort(); 컬렉션 정렬

마지막으로 다음을 수행했을 때 myList' = myList2참조 중 하나는 손실되었지만 원본은 손실되지 않았고 컬렉션은 정렬 된 상태로 유지되었습니다.

분실 된 참조

참조로 사용하는 경우 ( ref) myList'myList동일하게됩니다 (하나의 참조 만).

참고 : 사용 myList'하는 매개 변수를 나타내는 데 사용 ChangeList합니다 (원본과 동일한 이름을 지정 했으므로).


20

이해하는 쉬운 방법이 있습니다.

  • 목록은 힙에 생성 된 개체입니다. 변수 myList는 해당 개체에 대한 참조입니다.

  • C #에서는 개체를 절대 전달하지 않고 참조를 값으로 전달합니다.

  • ChangeList예를 들어 정렬 중에 전달 된 참조를 통해 목록 개체에 액세스하면 원래 목록이 변경됩니다.

  • ChangeList메서드 에 대한 할당 은 참조 값으로 이루어 지므로 원래 목록은 변경되지 않습니다 (여전히 힙에 있지만 메서드 변수에서 더 이상 참조되지 않음).


10

링크는 C #의 참조에 의한 전달을 이해하는 데 도움이됩니다. 기본적으로 참조 유형의 객체가 값으로 메소드에 전달되면 해당 객체에서 사용할 수있는 메소드 만 객체의 내용을 수정할 수 있습니다.

예를 들어 List.sort () 메서드는 List 내용을 변경하지만 다른 객체를 동일한 변수에 할당하면 해당 할당은 해당 메서드에 로컬입니다. 이것이 myList가 변경되지 않은 이유입니다.

ref 키워드를 사용하여 참조 유형의 객체를 전달하면 동일한 변수에 다른 객체를 할당 할 수 있으며 이는 전체 객체 자체를 변경합니다.

(편집 : 위의 링크 된 문서의 업데이트 된 버전입니다.)


5

C #은 문제의 개체가 실행되지 않는 한 값으로 전달 될 때 얕은 복사를 수행합니다 ICloneable(분명히 List클래스가 실행 하지 않음).

이것이 의미하는 바는 List자체를 복사 하지만 목록 내의 객체에 대한 참조는 동일하게 유지 된다는 것입니다 . 즉, 포인터는 원본과 동일한 객체를 계속 참조합니다 List.

List참조 항목의 값 을 변경 List하면 동일한 개체를 참조하므로 원본 도 변경 됩니다. 그러나 그런 다음 myList완전히 참조하는 것을 new 로 변경하면 List이제 원본 만 List해당 정수를 참조합니다.

자세한 내용은 "매개 변수 전달"에 대한 MSDN 문서의 참조 유형 매개 변수 전달 섹션을 읽어보십시오 .

StackOverflow의 "How do I Clone a Generic List in C #" 은 목록의 전체 복사본을 만드는 방법에 대해 설명합니다.


3

나는 모두가 위에서 말한 것에 동의하지만. 이 코드에 대해 다른 의견이 있습니다. 기본적으로 전역이 아닌 지역 변수 myList에 새 목록을 할당합니다. ChangeList (List myList)의 시그니처를 private void ChangeList ()로 변경하면 3, 4의 출력을 볼 수 있습니다.

여기에 내 추론이 있습니다 ... 목록이 참조로 전달되지만 포인터 변수를 값으로 전달하는 것으로 생각하십시오 ChangeList (myList)를 호출하면 포인터를 (Global) myList로 전달하는 것입니다. 이제 이것은 (local) myList 변수에 저장됩니다. 이제 (local) myList와 (global) myList가 동일한 목록을 가리키고 있습니다. 이제 정렬을 수행합니다 => (local) myList가 원래의 (global) myList를 참조하고 있기 때문에 작동합니다. 다음으로 새 목록을 만들고 (local) myList에 포인터를 할당합니다. 그러나 함수가 종료 되 자마자 (local) myList 변수는 파괴됩니다. HTH

class Test
{
    List<int> myList = new List<int>();
    public void TestMethod()
    {

        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList();

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList()
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

2

ref키워드를 사용하십시오 .

매개 변수 전달을 이해하려면 여기 에서 최종 참조를 확인 하십시오 .
구체적으로, 코드의 동작을 이해하려면 this를 보십시오 .

편집 : Sort동일한 참조 (값으로 전달됨)에서 작동하므로 값이 정렬됩니다. 그러나 매개 변수에 새 인스턴스를 할당하는 것은 ref.

Putting을 ref사용하면 사례의 새 인스턴스에 대한 참조에 대한 포인터를 변경할 수 있습니다 List. 가 없으면 ref기존 매개 변수에 대해 작업 할 수 있지만 다른 매개 변수를 가리킬 수 없습니다.


0

참조 유형의 개체에 할당 된 메모리의 두 부분이 있습니다. 하나는 스택에 있고 다른 하나는 힙에 있습니다. 스택의 부분 (일명 포인터)에는 실제 값이 저장되는 힙의 부분에 대한 참조가 포함됩니다.

ref 키워드를 사용하지 않으면 스택의 일부 사본 만 생성되어 메서드에 전달됩니다 (힙의 동일한 부분 참조). 따라서 힙 부분에서 무언가를 변경하면 해당 변경 사항이 유지됩니다. 복사 된 포인터를 변경하면-힙의 다른 위치를 참조하도록 할당하여-메서드 외부의 원점 포인터에 영향을주지 않습니다.

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