2008-10-16 43 views
8

Tôi có một lớp so sánh 2 trường hợp của cùng một đối tượng và tạo ra một danh sách các khác biệt của chúng. Điều này được thực hiện bằng cách lặp qua các bộ sưu tập chính và điền vào một tập hợp các bộ sưu tập khác với danh sách những gì đã thay đổi (điều này có thể có ý nghĩa hơn sau khi xem mã bên dưới). Điều này làm việc, và tạo ra một đối tượng cho phép tôi biết chính xác những gì đã được thêm vào và loại bỏ giữa đối tượng "cũ" và "mới".
Câu hỏi/mối quan tâm của tôi là ... nó thực sự xấu xí, với hàng tấn vòng và điều kiện. Có cách nào tốt hơn để lưu trữ/tiếp cận điều này, mà không cần phải dựa quá nhiều vào các nhóm vô tận của các điều kiện mã hóa cứng?Xóa các vòng lặp và điều kiện mã hóa lặp đi lặp lại trong C#

public void DiffSteps() 
    { 
     try 
     { 
      //Confirm that there are 2 populated objects to compare 
      if (NewStep.Id != Guid.Empty && SavedStep.Id != Guid.Empty) 
      { 
       //<TODO> Find a good way to compare quickly if the objects are exactly the same...hash? 

       //Compare the StepDoc collections: 
       OldDocs = SavedStep.StepDocs; 
       NewDocs = NewStep.StepDocs; 
       Collection<StepDoc> docstoDelete = new Collection<StepDoc>(); 

       foreach (StepDoc oldDoc in OldDocs) 
       { 
        bool delete = false; 
        foreach (StepDoc newDoc in NewDocs) 
        { 
         if (newDoc.DocId == oldDoc.DocId) 
         { 
          delete = true; 
         } 
        } 
        if (delete) 
         docstoDelete.Add(oldDoc); 
       } 

       foreach (StepDoc doc in docstoDelete) 
       { 
        OldDocs.Remove(doc); 
        NewDocs.Remove(doc); 
       } 


       //Same loop(s) for StepUsers...omitted for brevity 

       //This is a collection of users to delete; it is the collection 
       //of users that has not changed. So, this collection also needs to be checked 
       //to see if the permisssions (or any other future properties) have changed. 
       foreach (StepUser user in userstoDelete) 
       { 
        //Compare the two 
        StepUser oldUser = null; 
        StepUser newUser = null; 

        foreach(StepUser oldie in OldUsers) 
        { 
         if (user.UserId == oldie.UserId) 
          oldUser = oldie; 
        } 

        foreach (StepUser newie in NewUsers) 
        { 
         if (user.UserId == newie.UserId) 
          newUser = newie; 
        } 

        if(oldUser != null && newUser != null) 
        { 
         if (oldUser.Role != newUser.Role) 
          UpdatedRoles.Add(newUser.Name, newUser.Role); 
        } 

        OldUsers.Remove(user); 
        NewUsers.Remove(user); 
       } 

      } 
     } 
     catch(Exception ex) 
     { 
      string errorMessage = 
       String.Format("Error generating diff between Step objects {0} and {1}", NewStep.Id, SavedStep.Id); 
      log.Error(errorMessage,ex); 
      throw; 
     } 
    } 

Khung nhắm mục tiêu là 3.5.

Trả lời

7

Bạn đang sử dụng .NET 3.5? Tôi chắc chắn LINQ to Objects sẽ làm cho rất nhiều điều này nhiều hơn đơn giản hơn. Một điều khác cần suy nghĩ là nếu bạn có nhiều mã với một mẫu chung, chỉ một vài điều thay đổi (ví dụ: "Tôi đang so sánh tài sản nào?" Thì đó là một ứng cử viên tốt cho một phương pháp chung tham gia một đại biểu đại diện cho sự khác biệt đó

EDIT:. Được rồi, bây giờ chúng tôi biết chúng tôi có thể sử dụng LINQ:

Bước 1:. Giảm tổ
Trước hết tôi muốn đưa ra một mức độ làm tổ Thay vì :

if (NewStep.Id != Guid.Empty && SavedStep.Id != Guid.Empty) 
{ 
    // Body 
} 

tôi muốn làm:

if (NewStep.Id != Guid.Empty && SavedStep.Id != Guid.Empty) 
{ 
    return; 
} 
// Body 

lợi nhuận sớm như vậy có thể làm cho mã nhiều hơn nữa có thể đọc được.

Bước 2: Tìm tài liệu để xóa

này sẽ đẹp hơn nhiều nếu bạn chỉ đơn giản có thể chỉ định một chức năng quan trọng để Enumerable.Intersect. Bạn có thể chỉ định một bộ so sánh bình đẳng, nhưng việc xây dựng một trong số đó là một nỗi đau, ngay cả với một thư viện tiện ích. Ah tốt.

var oldDocIds = OldDocs.Select(doc => doc.DocId); 
var newDocIds = NewDocs.Select(doc => doc.DocId); 
var deletedIds = oldDocIds.Intersect(newDocIds).ToDictionary(x => x); 
var deletedDocs = oldDocIds.Where(doc => deletedIds.Contains(doc.DocId)); 

Bước 3: Loại bỏ các tài liệu
Hoặc sử dụng vòng lặp foreach hiện có, hoặc thay đổi các thuộc tính. Nếu thuộc tính của bạn thực sự thuộc loại < T> thì bạn có thể sử dụng RemoveAll.

Bước 4: Cập nhật và loại bỏ người dùng

foreach (StepUser deleted in usersToDelete) 
{ 
    // Should use SingleOfDefault here if there should only be one 
    // matching entry in each of NewUsers/OldUsers. The 
    // code below matches your existing loop. 
    StepUser oldUser = OldUsers.LastOrDefault(u => u.UserId == deleted.UserId); 
    StepUser newUser = NewUsers.LastOrDefault(u => u.UserId == deleted.UserId); 

    // Existing code here using oldUser and newUser 
} 

Một lựa chọn để đơn giản hóa mọi thứ hơn nữa sẽ được thực hiện một IEqualityComparer sử dụng UserId (và một cho tài liệu với DocId).

+0

Khung nhắm mục tiêu là 3.5. – Dan

0

Bạn đang nhắm mục tiêu theo khuôn khổ nào? (Điều này sẽ tạo ra sự khác biệt trong câu trả lời.)

Tại sao chức năng này bị hủy?

nên không phải là chữ ký như sau:

DiffResults results = object.CompareTo(object2); 
+0

Khung được nhắm mục tiêu là 3.5. Nó là một hàm void vì nó là các thuộc tính của DiffStep như OldDocs, v.v. – Dan

2

Khi bạn đang sử dụng ít nhất .NET 2.0 Tôi khuyên bạn nên thực hiện Equals và GetHashCode (http://msdn.microsoft.com/en-us/library/7h9bszxx.aspx) trên StepDoc. Là một gợi ý để làm thế nào nó có thể dọn dẹp mã của bạn bạn có thể có một cái gì đó như thế này:

Collection<StepDoc> docstoDelete = new Collection<StepDoc>(); 
foreach (StepDoc oldDoc in OldDocs) 
        { 
         bool delete = false; 
         foreach (StepDoc newDoc in NewDocs) 
         { 
          if (newDoc.DocId == oldDoc.DocId) 
          { 
           delete = true; 
          } 
         } 
         if (delete) docstoDelete.Add(oldDoc); 
        } 
        foreach (StepDoc doc in docstoDelete) 
        { 
         OldDocs.Remove(doc); 
         NewDocs.Remove(doc); 
        } 

với điều này:

oldDocs.FindAll(newDocs.Contains).ForEach(delegate(StepDoc doc) { 
         oldDocs.Remove(doc); 
         newDocs.Remove(doc); 
        }); 

này giả oldDocs là một danh sách của StepDoc.

0

Nếu bạn muốn ẩn traversal của cấu trúc cây như bạn có thể tạo một lớp con IEnumerator mà giấu đi những "xấu xí" cấu trúc lặp và sau đó sử dụng giao diện CompareTo:

MyTraverser t =new Traverser(oldDocs, newDocs); 

foreach (object oldOne in t) 
{ 
    if (oldOne.CompareTo(t.CurrentNewOne) != 0) 
    { 
     // use RTTI to figure out what to do with the object 
    } 
} 

Tuy nhiên, tôi không chắc chắn rằng điều này đặc biệt đơn giản hoá mọi thứ. Tôi không ngại nhìn thấy các cấu trúc đi qua lồng nhau. Mã được lồng vào nhau, nhưng không phức tạp hoặc đặc biệt khó hiểu.

1

Nếu cả hai StepDocs và StepUsers thi IComparable <T>, và chúng được lưu trữ trong bộ sưu tập mà thực hiện IList <T>, sau đó bạn có thể sử dụng phương thức helper sau đây để đơn giản hóa chức năng này. Chỉ cần gọi nó hai lần, một lần với StepDocs, và một lần với StepUsers. Sử dụng beforeRemoveCallback để thực hiện logic đặc biệt được sử dụng để cập nhật vai trò của bạn. Tôi giả sử các bộ sưu tập không chứa các bản sao. Tôi đã bỏ qua kiểm tra đối số.

public delegate void BeforeRemoveMatchCallback<T>(T item1, T item2); 

public static void RemoveMatches<T>(
       IList<T> list1, IList<T> list2, 
       BeforeRemoveMatchCallback<T> beforeRemoveCallback) 
    where T : IComparable<T> 
{ 
    // looping backwards lets us safely modify the collection "in flight" 
    // without requiring a temporary collection (as required by a foreach 
    // solution) 
    for(int i = list1.Count - 1; i >= 0; i--) 
    { 
    for(int j = list2.Count - 1; j >= 0; j--) 
    { 
     if(list1[i].CompareTo(list2[j]) == 0) 
     { 
     // do any cleanup stuff in this function, like your role assignments 
     if(beforeRemoveCallback != null) 
      beforeRemoveCallback(list[i], list[j]); 

     list1.RemoveAt(i); 
     list2.RemoveAt(j); 
     break; 
     } 
    } 
    } 
} 

Đây là một beforeRemoveCallback mẫu mã cập nhật của bạn:

BeforeRemoveMatchCallback<StepUsers> callback = 
delegate(StepUsers oldUser, StepUsers newUser) 
{ 
    if(oldUser.Role != newUser.Role) 
    UpdatedRoles.Add(newUser.Name, newUser.Role); 
}; 
0

Sử dụng nhiều danh sách trong foreach là dễ dàng. Làm điều này:

foreach (TextBox t in col) 
{ 
    foreach (TextBox d in des) // here des and col are list having textboxes 
    { 
     // here remove first element then and break it 
     RemoveAt(0); 
     break; 
    } 
} 

Nó hoạt động tương tự như nó là foreach (TextBox t trong col & & TextBox d trong des)

Các vấn đề liên quan