2008-10-29 38 views
47

Tôi không nghĩ điều này là có thể, nhưng nếu sau đó tôi cần nó :)Ghi đè hàm dựng mặc định của một phần lớp với một lớp khác

Tôi có tệp proxy được tạo tự động từ dòng lệnh wsdl.exe công cụ của Visual Studio 2008.

Kết quả đầu ra proxy là một phần các lớp. Tôi muốn ghi đè lên constructor mặc định được tạo ra. Tôi không muốn sửa đổi mã vì nó được tạo tự động.

Tôi đã thử tạo một lớp khác và định nghĩa lại hàm tạo mặc định, nhưng điều đó không hoạt động. Sau đó tôi đã thử sử dụng ghi đè và từ khóa mới, nhưng điều đó không hoạt động.

Tôi biết tôi có thể kế thừa từ lớp học một phần, nhưng điều đó có nghĩa là tôi phải thay đổi tất cả mã nguồn để trỏ đến lớp cha mẹ mới. Tôi không muốn phải làm điều này.

Bất kỳ ý tưởng, xung quanh công việc hoặc hack nào?

//Auto-generated class 
namespace MyNamespace { 
    public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol { 
     public MyWebService() { 
     string myString = "auto-generated constructor"; 
     //other code... 
     } 
    } 
} 

//Manually created class in order to override the default constructor 
namespace MyNamespace { 
    public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol { 
     public override MyWebService() { //this doesn't work 
     string myString = "overridden constructor"; 
     //other code... 
     } 
    } 
} 

Trả lời

36

Điều này là không thể. Các lớp học một phần cơ bản là các phần của cùng một lớp; không có phương thức nào có thể được định nghĩa hai lần hoặc bị ghi đè và bao gồm cả hàm tạo.

Bạn có thể gọi phương thức trong hàm tạo và chỉ triển khai nó trong tệp phần khác.

0

Không có gì mà tôi có thể nghĩ đến. Cách "tốt nhất" tôi có thể đưa ra là thêm một ctor với thông số giả và sử dụng:

public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol 
{ 
    public override MyWebService(int dummy) 
    { 
     string myString = "overridden constructor"; 
     //other code... 
    } 
} 


MyWebService mws = new MyWebService(0); 
2

Bạn không thể thực hiện việc này. Tôi đề nghị sử dụng một phần phương pháp mà bạn có thể sau đó tạo ra một định nghĩa cho. Một cái gì đó như:

public partial class MyClass{ 

    public MyClass(){ 
     ... normal construction goes here ... 
     AfterCreated(); 
    } 

    public partial void OnCreated(); 
} 

Phần còn lại nên khá tự giải thích.

EDIT:

Tôi cũng muốn chỉ ra rằng bạn nên xác định một giao diện cho dịch vụ này, mà bạn có thể sau đó chương trình, vì vậy bạn không cần phải có tài liệu tham khảo để thực hiện thực tế. Nếu bạn đã làm điều này thì bạn sẽ có một vài tùy chọn khác.

2

Tôi nghĩ bạn có thể làm điều này với PostSharp và có vẻ như ai đó đã thực hiện chỉ những gì bạn want for methods in generated partial classes. Tôi không biết nếu điều này sẽ dễ dàng dịch sang khả năng viết một phương pháp và có cơ thể của nó thay thế các nhà xây dựng như tôi đã không cho nó một shot nhưng có vẻ như giá trị một shot.

Chỉnh sửa: this is along the same lines và cũng có vẻ thú vị.

66

Tôi đã có một prolem tương tự, với mã được tạo của tôi đang được tạo bởi một tệp dbml (tôi đang sử dụng các lớp LINQ-to-SQL).

Trong lớp được tạo, nó gọi một phần void được gọi là OnCreated() ở cuối hàm tạo.

câu chuyện dài ngắn, nếu bạn muốn giữ lại những thứ constructor quan trọng lớp được tạo ra làm cho bạn (mà có lẽ bạn nên làm), sau đó trong partial class của bạn tạo ra như sau:

partial void OnCreated() 
{ 
    // Do the extra stuff here; 
} 
+1

+1 Giải pháp đơn giản và thanh lịch. – James

+3

Bây giờ đây là một tình trạng khó xử bỏ phiếu ... không thực sự liên quan gì đến câu hỏi OP mà không phải về L2S vì vậy sẽ không có OnCreated nhưng bạn đã ngăn tôi đập đầu vào bàn vì vậy +1 tôi nghĩ. – Ryan

+0

@Ryan: Rất vui khi được giúp đỡ. Cảm ơn bạn :-) –

12

Hmmm, tôi nghĩ rằng một giải pháp thanh lịch sẽ là như sau:

//* AutogenCls.cs file 
//* Let say the file is auto-generated ==> it will be overridden each time when 
//* auto-generation will be triggered. 
//* 
//* Auto-generated class, let say via xsd.exe 
//* 
partial class AutogenCls 
{ 
    public AutogenCls(...) 
    { 
    } 
} 



//* AutogenCls_Cunstomization.cs file 
//* The file keeps customization code completely separated from 
//* auto-generated AutogenCls.cs file. 
//* 
partial class AutogenCls 
{ 
    //* The following line ensures execution at the construction time 
    MyCustomization m_MyCustomizationInstance = new MyCustomization(); 

    //* The following inner&private implementation class implements customization. 
    class MyCustomization 
    { 
     MyCustomization() 
     { 
      //* IMPLEMENT HERE WHATEVER YOU WANT TO EXECUTE DURING CONSTRUCTION TIME 
     } 
    } 
} 

cách tiếp cận này có một số nhược điểm (như tất cả mọi thứ):

  1. Không rõ khi nào chính xác sẽ được thực thi hàm tạo của lớp bên trong MyCustomization trong toàn bộ quy trình xây dựng của lớp AutogenCls.

  2. Nếu cần thiết để triển khai giao diện IDIPosable cho lớp MyCustomization để xử lý chính xác việc loại bỏ tài nguyên không được quản lý của lớp MyCustomization, tôi chưa biết cách kích hoạt phương thức MyCustomization.Dispose() file AutogenCls.cs ... (nhưng như tôi đã nói 'yet' :)

nhưng phương pháp này cung cấp tách lớn từ mã tự động tạo ra - toàn tùy biến được tách ra trong tập tin mã src khác nhau.

tận hưởng :)

+0

StyleCop sẽ khiếu nại về giải pháp này: bạn nên tránh các biến riêng tư không sử dụng. –

+1

Giải pháp này chỉ cung cấp một hàm tạo tĩnh: không truy cập vào 'this'. –

+0

Một đơn giản hóa nhỏ, thay vì một 'lớp MyCustomization', bạn chỉ cần khai báo' Task _customization = TaskEx.Run (async() => {/ * Do customization * /}); '. Và 'async' có thể được ommited nếu bạn không cần nó. –

1

Đây là ý kiến ​​của tôi về lỗi thiết kế trong ngôn ngữ. Họ nên đã cho phép nhiều triển khai của một phương pháp một phần, mà có thể đã cung cấp một giải pháp tốt đẹp. Trong một cách thậm chí đẹp hơn, hàm tạo (cũng là một phương thức) có thể đơn giản được đánh dấu một phần và nhiều hàm tạo với cùng một chữ ký sẽ chạy khi tạo một đối tượng.

Giải pháp đơn giản nhất có lẽ là để thêm một phương pháp 'constructor' phần mỗi thêm partial class:

public partial class MyClass{ 

    public MyClass(){ 
     ... normal construction goes here ... 
     OnCreated1(); 
     OnCreated2(); 
     ... 
    } 

    public partial void OnCreated1(); 
    public partial void OnCreated2(); 
} 

Nếu bạn muốn các lớp học phần là bất khả tri về nhau, bạn có thể sử dụng phản ánh:

// In MyClassMyAspect1.cs 
public partial class MyClass{ 

    public void MyClass_MyAspect2(){ 
     ... normal construction goes here ... 

    } 

} 

// In MyClassMyAspect2.cs 
public partial class MyClass{ 

    public void MyClass_MyAspect1(){ 
     ... normal construction goes here ... 
    } 
} 

// In MyClassConstructor.cs 
public partial class MyClass : IDisposable { 

    public MyClass(){ 
     GetType().GetMethods().Where(x => x.Name.StartsWith("MyClass")) 
          .ForEach(x => x.Invoke(null)); 
    } 

    public void Dispose() { 
     GetType().GetMethods().Where(x => x.Name.StartsWith("DisposeMyClass")) 
          .ForEach(x => x.Invoke(null)); 
    } 

} 

Nhưng thực sự họ chỉ nên thêm một số cấu trúc ngôn ngữ khác để hoạt động với các lớp một phần.

0

Đối với một proxy dịch vụ Web được tạo bởi Visual Studio, bạn không thể thêm hàm tạo riêng của mình trong lớp một phần (bạn có thể, nhưng nó không được gọi). Thay vào đó, bạn có thể sử dụng thuộc tính [OnDeserialized] (hoặc [OnDeserializing]) để móc trong mã của riêng bạn tại điểm mà lớp proxy web được khởi tạo.

using System.Runtime.Serialization; 

partial class MyWebService 
{ 
    [OnDeserialized] 
    public void OnDeserialized(StreamingContext context) 
    { 
     // your code here 
    } 
} 
+0

Đây có phải là dịch vụ web hoặc cho các đối tượng đang được deserialized khi chúng được trả lại trong một cuộc gọi dịch vụ? Tôi đã thử thêm nó vào lớp một phần của khách hàng dịch vụ web của tôi, nhưng phương pháp của tôi không được gọi ... –

4

Thực tế, bây giờ có thể, bây giờ đã thêm một phần phương pháp. Đây là doc:

http://msdn.microsoft.com/en-us/library/wa80x488.aspx

Về cơ bản, ý tưởng là bạn có thể khai báo và gọi một phương thức trong một tập tin mà bạn đang định lớp học phần, nhưng không thực sự xác định phương pháp trong tập tin đó. Trong tệp khác, bạn có thể xác định phương thức. Nếu bạn đang xây dựng một hội đồng mà phương thức không được xác định, thì ORM sẽ xóa tất cả các cuộc gọi đến hàm.

Vì vậy, trong trường hợp trên nó sẽ trông như thế này:

// Tự động tạo ra lớp

namespace MyNamespace { 
    public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol { 
     public MyWebService() { 
     string myString = "auto-generated constructor"; 
     OtherCode(); 
     } 
    } 
} 

partial void OtherCode(); 

// thủ tạo ra lớp để ghi đè lên các constructor mặc định

partial void OtherCode() 
{ 
    //do whatever extra stuff you wanted. 
} 

Nó có phần hạn chế, và trong trường hợp cụ thể này, nơi bạn có một tệp được tạo mà bạn cần thay đổi, nó có thể không phải là giải pháp đúng, nhưng đối với những người khác tình cờ gặp phải để ghi đè lên chức năng trong các lớp học một phần, điều này có thể khá hữu ích.

+5

Vấn đề lớn là mã được tạo tự động phải thực hiện điều này, nhưng trong nhiều trường hợp, tôi không có quyền kiểm soát mã tự động – VoteCoffee

0

Đôi khi bạn không có quyền truy cập hoặc không được phép thay đổi hàm tạo mặc định, vì lý do này bạn không thể có hàm tạo mặc định để gọi bất kỳ phương thức nào.

Trong trường hợp này bạn có thể tạo một constructor với một tham số giả, và làm cho nhà xây dựng mới này để gọi constructor mặc định sử dụng ": đây()"

public SomeClass(int x) : this() 
{ 
    //Your extra initialization here 
} 

Và khi bạn tạo một đối tượng mới này lớp học mà bạn chỉ cần vượt qua tham số giả như thế này:

SomeClass objSomeClass = new SomeClass(0); 
2

vấn đề mà OP đã có là proxy tham khảo web không tạo ra bất kỳ phương pháp từng phần mà bạn có thể sử dụng để đánh chặn các nhà xây dựng.

Tôi đã gặp sự cố tương tự và tôi không thể nâng cấp lên WCF vì dịch vụ web mà tôi đang nhắm mục tiêu không hỗ trợ nó.

Tôi không muốn sửa đổi mã được tạo tự động theo cách thủ công bởi vì nó sẽ bị làm phẳng nếu có ai đó gọi đến việc tạo mã.

Tôi giải quyết vấn đề từ một góc độ khác. Tôi biết khởi tạo của tôi cần làm trước khi một yêu cầu, nó không thực sự cần phải được thực hiện tại thời gian xây dựng, vì vậy tôi chỉ cần overrode phương pháp GetWebRequest như vậy.

protected override WebRequest GetWebRequest(Uri uri) 
{ 
    //only perform the initialization once 
    if (!hasBeenInitialized) 
    { 
     Initialize(); 
    } 

    return base.GetWebRequest(uri); 
} 

bool hasBeenInitialized = false; 

private void Initialize() 
{ 
    //do your initialization here... 

    hasBeenInitialized = true; 
} 

Đây là một giải pháp tốt đẹp bởi vì nó không liên quan đến hack tự động tạo mã, và nó phù hợp với trường hợp sử dụng chính xác của OP thực hiện khởi tạo đăng nhập cho một SoapHttpClientProtocol tự động proxy được tạo ra.

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