2010-06-28 32 views
5

Xin lỗi vì tiêu đề mơ hồ, nhưng tôi không chắc cách tóm tắt điều này trong một cụm từ. Tôi có một tình huống với rất nhiều mã C# dư thừa, và nó thực sự trông giống như một số loại lừa crafty sử dụng một số tài sản thừa kế hoặc generics sẽ giải quyết này. Tuy nhiên, tôi không phải là một lập trình viên kinh nghiệm khủng khiếp (đặc biệt là với C#) và chỉ không thể nhìn thấy giải pháp.Gặp sự cố khi loại bỏ mã dư thừa thông qua kế thừa hoặc generics

Tình huống, ở dạng đơn giản, trông giống như thế này. Tôi có một loạt các lớp học mà tất cả thừa kế từ một loại.

public class Foo : SuperFoo 
{ 
    ... 
    public Foo SomeMethod() { ... } 
} 
public class Bar : SuperFoo 
{ 
    ... 
    public Bar SomeMethod() { ... } 
} 
public class Baz : SuperFoo 
{ 
    ... 
    public Baz SomeMethod() { ... } 
} 
...  
public class SuperFoo 
{ 
    ... 
} 

Sự cố xảy ra khi thu thập các đối tượng này cần được xử lý. Giải pháp dự thảo đầu tiên của tôi (giải pháp xấu) trông giống như sau:

public void SomeEventHasHappened(...) 
{ 
    ProcessFoos(); 
    ProcessBars(); 
    ProcessBazes(); 
    ... 
} 

public void ProcessFoos() 
{ 
    ... 
    foreach (var foo in fooList) 
    { 
      ... 
      foo.SomeMethod(); 
    } 
} 
public void ProcessBars() 
{ 
    ... 
    foreach (var bar in barList) 
    { 
      ... 
      bar.SomeMethod(); 
    } 
} 

... v.v. Vấn đề là về cơ bản tất cả các mã trong các phương thức ProcessX là giống nhau, khác với kiểu của các đối tượng đang được vận hành. Nó sẽ là tốt đẹp để củng cố tất cả các thành một phương pháp vì lý do rõ ràng.

Suy nghĩ đầu tiên của tôi là chỉ cần tạo một phương thức Process() chung lấy một tham số List<SuperFoo> và chỉ tiếp tục từ đó. Vấn đề là một SuperFoo chung không có SomeMethod(), và nó không thể có một vì mỗi lớp con 'SomeMethod() có kiểu trả về khác nhau, vì vậy việc ghi đè không hoạt động.

+0

Tôi đã thấy mã nơi 'đối tượng' được trả về và sau đó truyền đến loại thích hợp. –

+0

Trong khi đó sẽ giải quyết vấn đề như tôi đã trình bày nó ở đây, tôi nên làm rõ ... Tôi muốn loại không có 'SomeMethod()' trả về một 'đối tượng chung', bởi vì' SomeMethod() 'cũng được gọi trong nhiều nơi khác ngoài phần này của mã, và điều đó đòi hỏi phải đúc ở tất cả những nơi khác. Nhờ đề nghị mặc dù. – jloubert

Trả lời

2

Tôi thường thêm giao diện hoạt động trên các loại cơ sở.

interface ISuperFoo 
{ 
    public ISuperFoo SomeMethod() { ... } 
} 

public class Foo : SuperFoo, ISuperFoo 
{ 
    // concrete implementation 
    public Foo SomeMethod() { ... } 

    // method for generic use, call by base type 
    public ISuperFoo ISuperFoo.SomeMethod() 
    { 
     return SomeMethod(); 
    } 
} 

public void Processs() 
{ 
    ... 
    foreach (var iSuperFoo in list) 
    { 
      ... 
      iSuperFoo.SomeMethod(); 
    } 
} 

Tất nhiên nó phụ thuộc vào những gì bạn đang sử dụng kết quả.

Đôi khi bạn có thể làm cho việc sử dụng Generics dễ dàng hơn, nhưng bạn cũng có thể kết thúc trong một mớ hỗn độn. Đôi khi nó chỉ là dễ dàng hơn để downcast một nơi nào đó. Tất nhiên, bạn cố gắng tránh điều này bất cứ khi nào bạn có thể mua được.

+0

Điều này là khá tốt đẹp, và có lẽ một cái gì đó tôi nên có suy nghĩ! Cảm ơn câu trả lời. – jloubert

2

Dưới đây là ví dụ về cách điều này có thể hoạt động bằng cách sử dụng Generics và làm cho SuperFoo trở thành một lớp trừu tượng.

public interface ISuperFoo 
{ 
    ... 
} 

public abstract class SuperFoo<T> where T : ISuperFoo 
{ 
    public abstract T SomeMethod(); 
} 

public class BazReturn : ISuperFoo 
{ 
    ... 
} 

public class Baz: SuperFoo<BazReturn> 
{ 
    public override BazReturn SomeMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class BarReturn : ISuperFoo 
{ 
    ... 
} 

public class Bar : SuperFoo<BarReturn> 
{ 
    public override BarReturn SomeMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public static class EventHandler 
{ 
    public static void SomeEventHasHappened(List<SuperFoo<ISuperFoo>> list) 
    { 
     foreach (SuperFoo<ISuperFoo> item in list) 
     { 
      ISuperFoo result = item.SomeMethod(); 
     } 
    } 
} 

Bạn có thể thay thế giao diện ISuperFoo bằng lớp bê tông nếu cần, nhưng sau đó bạn sẽ phải truyền giá trị trả về loại mục đích.

public abstract class SuperFoo<T> 
{ 
    public abstract T SomeMethod(); 
} 

public class Foo : SuperFoo<int> 
{ 
    public override int SomeMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public static class EventHandler 
{ 
    public static void SomeEventHasHappened(List<SuperFoo<int>> list) 
    { 
     foreach (SuperFoo<int> item in list) 
     { 
      item.SomeMethod(); 
     } 
    } 
} 
+0

Cảm ơn bạn đã phản hồi. Nhưng tôi hơi bối rối bởi các lớp FooReturn và Foo trong phần đầu của câu trả lời của bạn. Nếu tôi hiểu điều này một cách chính xác, có vẻ như một thể hiện của SomeMethod của Foo() sẽ trả về một đối tượng FooReturn thay vì một đối tượng Foo. Vì vậy, do đó, tất cả các mã khác được sử dụng để được trong Foo bây giờ nên được trong FooReturn, trong khi Foo chỉ cần chứa SomeMethod(). Nếu điều này thực sự là trường hợp, sẽ không phá vỡ mã này ở một số nơi khác có 'Foo fooObject = anotherFooObject.SomeMethod();'? – jloubert

+0

Loại trả về của SomeMethod phải luôn được coi là trả về ISuperFoo. Bạn sẽ nhận thấy rằng varialbe T chung của SuperFoo trừu tượng phải mở rộng ISuperFoo.Đây là những gì mang lại kiểu trả về nhất quán được yêu cầu. Tôi đã thêm một ví dụ Bar vào mã của tôi mà hy vọng sẽ làm rõ. – sgriffinusa

+0

Tôi cũng đổi tên lớp con từ Foo thành Baz, hy vọng sẽ làm rõ thêm sự khác biệt giữa giao diện, trừu tượng và các loại mở rộng. – sgriffinusa

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