2013-03-08 5 views
6

일부 C# 코드에서 이상한 동작을 보았습니다. 설명하기가 어려웠습니다. 나는 중요한 이해를 잃어 버릴 수 있었을 것입니다. 그래서 누군가가 저를 위해 빛을 바꿀 수 있기를 바랍니다. 당신이 그것을 통해 단계 때C#이 IEnumerable 내에서 속성을 설정하지 못했습니다.

IEnumberable<myObject> objects = GetObjectsFromApiCall(); 

    for (int i = 0; i < objects.Count(); i++) 
     { 
      if (String.IsNullOrEmpty(objects.ElementAt(i).SubObject.Title)) 
      { 
       SubObject sub = GetSubObjectFromDatabase((long)objects.ElementAt(i).SubObject.Id); 
       if (sub != null) 
       { 
        objects.ElementAt(i).SubObject.Title = sub.Title; 
       } 
      } 
     } 

,이 코드에 대한 모든 것이 제대로 작동하는 것 같다 :

은 다음과 같습니다 코드 블록을 얻었다. "objects"컬렉션은 예상대로 채워집니다. "sub"는 수집 된 것으로 가져오고 채워진 Title 속성을 포함하여 예상되는 전체 속성 집합을 포함합니다. 실행 중에 오류가 발생하지 않습니다.

하지만 각 Object에 완고하게 존재하는 SubObject.Title 속성 (표준 get; set; 코드 만 있음)은 비어 있습니다.

나는 손실에 처해있다. 아무도 무슨 일이 일어나고 있는지 설명해 주시겠습니까?

EDIT : for 루프와 ElementAt를 사용해서는 안되는 제안은 foreach 루프로 시작했지만 매번 새로운 SubObject를 가져 오는 것이므로 문제의 원인이 될 수 있다고 생각했습니다. 귀하의 도움으로 고쳐졌으며 ForEach가 복원되었습니다.

환호, 매트 모든

+4

다음과 같이하십시오. [IEnumerable 내에서 항목 속성을 업데이트 하나 해당 속성이 설정되지 않은 채 있습니까?] (0120-385-3310) –

+0

이 코드는 매우 느려질 수 있습니다. 웃기지도 않아. – ChaosPandion

+0

* 실제 * 코드처럼 * 실제 * 코드를 복사하거나 붙여 넣을 수 있습니까? – ken2k

답변

4

내가 이런 식으로 해결하는 것입니다 :

var objects = GetObjectsFromApiCall().ToList(); 

이 그럼 당신은 같이 (작동) 루프를 유지하거나 다른 답변에 의해 제안으로 조금 사용 foreach는 일부 Linq에 최적화 수를하지만 않습니다 별로 문제가되지 않았습니다. IEnumerator의 요소를 변경하려고 시도했습니다. <> @Ahmet Kakıcı가 가리키는 this question에 설명되어 있습니다. 객체를 얻을 수 objects.ElementAt (내가) 내가 옳다 경우

public IEnumberable<myObject> GetObjectsFromApiCall(){ 
    for(var i = 0; i < 10; i++) 
    { 
     yield return new myObject(); 
    } 
} 

은, 때마다 당신이 전화 기능 :

+1

-1 이것은 실제로 잘못되었습니다. IEnumerable에 의해 반환 된 요소를 수정하는 데 아무런 문제가 없습니다. 실제로'ToList()'뒤에'foreach '를 사용하면'List '는'IEnumerable '을 구현하기 때문에 정확하게 IEnumerable을 사용하게됩니다. 한 가지 문제는 DB 쿼리의 지연된 실행 일 수 있습니다. 그러나 IEnumerable 인터페이스의 단순한 존재 때문이 아닙니다. – ken2k

+0

맞습니다. 너무 빠릅니다 ... 문제는 IEnumerable이 아닙니다. 하지만 그것이 구현되는 방식입니다. 그래서 ToList()를 사용하는 것이 좋습니다. 해명 해줘서 고마워. – Larry

+0

@ ken2k 수정. IEnumerable에 의해 반환 된 항목을 수정하는 예제는 내 대답을 참조하십시오. –

1

먼저, 코드의 이런 종류의 ElementAt()을 사용하여 사용하지 말아야 또한주의해야

foreach (var o in objects) 
{ 
    if (string.IsNullOrEmpty(o.SubObject.Title)) 
    { 
     o.SubObject.Title = ...; 
    } 
} 

당신의 방법은 다음마다 동적 IEnumerable을 반환하는 경우 objects.Something()으로 호출하면 API가 다시 호출되고 새로운 복사본이 검색됩니다. 이 경우 .ToList() 메서드를 사용하여 열거 형을 목록에 복사해야합니다. 이 같은 동적 열거를 생성하여 -

또한 목록에없는 복사본을 넣어하는 방법이있다 :하지 (이전 일이 도움이되지 않은 경우)이 올바르게 설정되는 값으로

objects = objects.Select(o => 
{ 
    if (string.IsNullOrEmpty(o.SubObject.Title)) 
    { 
     o.SubObject.Title = ...; 
    } 
    return o; 
}); 

- Title 속성에 대한 설정자에 throw new Exception(value)을 추가하십시오. 올바른 값으로 호출되는지 확인하십시오.

+0

"우선, 이런 종류의 코드에는 ElementAt()를 사용하면 안됩니다." 왜? –

+0

.NET은'Enumerator.MoveNext()'를 사용하여 매번 값을 얻기 위해 'i'번 열거합니다. 'list [i]'접근법보다 느리다. –

2

기능 GetObjectsFromApiCall은 다음과 같습니다이

List<myObject> objects = GetObjectsFromApiCall().ToList(); 

foreach(var obj in objects.Where(o => string.IsNullOrEmpty(objects.SubObject.Title)).ToList()) 
{ 
    var subObject = GetSubObjectFromDatabase(obj.SubObject.Id); 
    if(subObject == null) continue; 

    obj.SubObject.Title = subObject.Title; 
} 
1

I 게스트 시도 , 당신은 "yield return new myObject()"로 새 객체를 얻을 것입니다.

+0

그것은 좋은 이론입니다. 나는 그와 같은 예제를 게시 할 생각이었습니다. –

+0

아, "objects.ElementAt (i) .SubObject.Title = sub.Title;"을 변경해야합니다. "var obj = objects.ElementAt (i) .SubObject; obj.Title = sub.Title;" – fengyj

+0

귀하의 의견에 관하여 : 그것은 어떻게 바뀌겠습니까? –

1

그러나 Title 속성이 변경되었는지 어떻게 확인합니까? GetObjectsFromApiCall() 번으로 다시 전화 하시겠습니까? 또는 을 통해 objects 인스턴스를 다시 수행 하시겠습니까?

IEnumerable 인스턴스는 "열거 된"때마다 새 개체를 만들고 생성 할 수 있습니다. 여기에 간단한 예제가 있습니다. 예를 들어, 정의

static IEnumerable<SomeObject> GetSomeSequence() 
    { 
     yield return new SomeObject { Title = "Alpha", }; 
     yield return new SomeObject { Title = "Beta", }; 
     yield return new SomeObject { Title = "Gamma", }; 
    } 

그런 다음 이런 식으로 시험 :

class SomeObject 
{ 
    public string Title { get; set; } 
} 

그렇다면 우리는 두 개의 "소스"유형의 제 배열과 같이 정의 다음 반복기 블록을 고려한다 :

static void Main() 
    { 
     IEnumerable<SomeObject> thingsToModify; 

     // set source to an array 
     thingsToModify = new[] { new SomeObject { Title = "Alpha", }, new SomeObject { Title = "Beta", }, new SomeObject { Title = "Gamma", }, }; 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); 

     foreach (var t in thingsToModify) 
      t.Title = "Changed!"; 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); // OK, modified 


     // set source to something which yields new object each time a new GetEnumerator() call is made 
     thingsToModify = GetSomeSequence(); 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); 

     foreach (var t in thingsToModify) 
      t.Title = "Changed!";   // no-one keeps these modified objects 

     foreach (var t in thingsToModify) 
      Console.WriteLine(t.Title); // new objects, titles not modified 

    } 

결론 : 우리가 반복하는 소스에 속하는 가변 객체의 상태를 수정할 수 있습니다. 그러나 일부 유형의 IEnumerable 소스는 호출 될 때마다 데이터의 새 복사본을 생성하므로 복사본을 수정하는 것은 유용하지 않습니다.

관련 문제