Làm cách nào để triển khai các phương thức tĩnh trên một giao diện?


91

Tôi có một C ++ DLL của bên thứ ba mà tôi gọi từ C #.

Các phương thức là tĩnh.

Tôi muốn tóm tắt nó để thực hiện một số thử nghiệm đơn vị, vì vậy tôi đã tạo một giao diện với các phương thức tĩnh trong đó nhưng bây giờ chương trình của tôi bị lỗi với:

Công cụ sửa đổi 'tĩnh' không hợp lệ cho mục này

MyMethod cannot be accessed with an instance reference; qualify it with a type name instead

Làm thế nào tôi có thể đạt được sự trừu tượng này?

Mã của tôi trông như thế này

private IInterfaceWithStaticMethods MyInterface;

public MyClass(IInterfaceWithStaticMethods myInterface)
{
  this.MyInterface = myInterface;
}

public void MyMethod()
{
  MyInterface.StaticMethod();
}

3
Có thể bạn có thể làm điều đó bằng các phương pháp mở rộng: stackoverflow.com/questions/1243921/…
hcb

Câu trả lời:


47

Bạn không thể xác định các thành viên tĩnh trên giao diện trong C #. Giao diện là một hợp đồng cho các phiên bản .

Tôi khuyên bạn nên tạo giao diện như hiện tại nhưng không có từ khóa tĩnh. Sau đó, tạo một lớp StaticIInterfacethực thi giao diện và gọi các phương thức C ++ tĩnh. Để thực hiện kiểm thử đơn vị, hãy tạo một lớp khác FakeIInterface, lớp này cũng triển khai giao diện, nhưng thực hiện những gì bạn cần để xử lý các kiểm thử đơn vị của mình.

Khi bạn đã xác định 2 lớp này, bạn có thể tạo một lớp bạn cần cho môi trường của mình và chuyển nó cho hàm tạo MyClasscủa.


64
-1 để nói An interface is a contract, not an implementation.- đó là sự thật, nhưng hoàn toàn không liên quan ( không phải sequitur ) ở đây, vì phương thức tĩnh không phải là một phần của bản thân việc triển khai - theo định nghĩa, việc triển khai dựa trên dữ liệu , do đó, không thể truy cập được đối với các thành viên tĩnh. An interface type definition can define and implement static methods (see §8.4.3) since static methods are associated with the interface type itself rather than with any value of the type.- hãy nhớ rằng staticcác thành viên thường là các phương thức hữu ích .

2
Tôi hiểu và đồng ý với tuyên bố của bạn, và tôi cảm thấy nhận xét của bạn cũng là một ngữ cảnh quan trọng. Mặc du. Khi thiết kế một giao diện, người ta nên nghĩ về nó như một hợp đồng, điều này ngụ ý rằng các phương pháp tĩnh không được áp dụng. Tôi nghĩ rằng tôi nên để nó ở đó để giúp một số người hiểu mục đích của giao diện. Cộng đồng có cảm thấy nó nên bị xóa không?
davisoa

1
Tôi đồng ý một phần rằng điều đó An interface is a contract, not an implementationlà vô ích, đôi khi một chút ngữ cảnh thực sự có ích. Và tôi hoàn toàn đồng ý với static method is not a part of implementation itself , các phương thức tĩnh một phần triển khai, chúng chỉ trở thành một phần của việc thực thi nếu được sử dụng như một phần triển khai trong việc thực hiện một phương thức khác. Tuy nhiên từ điển của tôi dựa trên những gì đã học, theo như tôi biết, thuật ngữ thực sự khác nhau tùy thuộc vào ngôn ngữ lập trình. Các phương thức tĩnh không thể là giao diện vì dù sao cũng có thể chỉ có 1 triển khai.
CoffeDeveloper

Hãy tưởng tượng tôi có một IPersonhợp đồng quy định rằng GetCountrysẽ cung cấp tên quốc gia gốc của người đó ... FrenchPersoncác thực thể sẽ nói "Pháp" và GermanPersontất cả sẽ nói "Đức", cũng hữu ích khi các loại thực thể khác nhau chia sẻ cùng một Bảng (Dữ liệu), như MS Azure một, chẳng hạn Connection, PostCommentđược lưu trữ trong UsersAzureTable, vì vậy các thực thể cây có thông tin được chia sẻ, IUserscó thể có GetTableNamephương thức tĩnh ...
Serge

@vaxquis - IMHO, "hợp đồng của nó" sẽ có liên quan nếu câu được sửa lại: ʻAn interface là hợp đồng cho các phiên bản . Thành viên tĩnh là một phần của kiểu; câu được sửa lại này nói (một cách chính xác) rằng chúng không có ý nghĩa gì trong một hợp đồng cá thể. Vì vậy, tôi nghĩ vấn đề chỉ đơn thuần là cách diễn đạt không chính xác, không phải là không có trình tự.
ToolmakerSteve

112

Các giao diện không thể có các thành viên tĩnh và các phương thức tĩnh không thể được sử dụng để triển khai các phương thức giao diện.

Những gì bạn có thể làm là sử dụng triển khai giao diện rõ ràng:

public interface IMyInterface
{
    void MyMethod();
}

public class MyClass : IMyInterface
{
    static void MyMethod()
    {
    }

    void IMyInterface.MyMethod()
    {
        MyClass.MyMethod();
    }
}

Ngoài ra, bạn có thể chỉ cần sử dụng các phương thức không tĩnh, ngay cả khi chúng không truy cập vào bất kỳ thành viên cụ thể nào.


18
Đối với bất kỳ ai thắc mắc tại sao bất kỳ ai lại muốn làm điều này, nó đặc biệt hữu ích khi viết các bài kiểm tra đơn vị / tích hợp cho mã kế thừa triển khai các phương thức tĩnh.
Dezzamondo

Kỹ thuật này hoạt động rất hiệu quả để triển khai một API RESTful nhanh cần duy trì dữ liệu nhưng không thể sử dụng cơ sở dữ liệu. Việc triển khai chỉ hoạt động với các đối tượng C # trong bộ nhớ nên không có chỗ để lưu trữ dữ liệu, nhưng việc sử dụng thuộc tính tĩnh đã làm giảm bớt nhu cầu về cơ sở dữ liệu trong bộ nhớ bằng EF Core hoặc SQLite.
gware

19

Các thành viên tĩnh hoàn toàn hợp pháp trong CLR, chỉ không phải C #.

Bạn có thể triển khai một số keo trong IL để liên kết các chi tiết thực hiện.

Không chắc liệu trình biên dịch C # có cho phép gọi chúng hay không?

Xem: 8.9.4 Định nghĩa kiểu giao diện ECMA-335.

Các kiểu giao diện nhất thiết không hoàn chỉnh vì chúng không nói gì về việc biểu diễn các giá trị của kiểu giao diện. Vì lý do này, một định nghĩa kiểu giao diện sẽ không cung cấp định nghĩa trường cho các giá trị của kiểu giao diện (ví dụ, trường thể hiện), mặc dù nó có thể khai báo các trường tĩnh (xem §8.4.3).

Tương tự, một định nghĩa kiểu giao diện sẽ không cung cấp triển khai cho bất kỳ phương pháp nào trên các giá trị của kiểu của nó. Tuy nhiên, định nghĩa kiểu giao diện có thể — và thường là — xác định các hợp đồng phương thức (tên phương thức và chữ ký phương thức) sẽ được thực hiện bởi các kiểu hỗ trợ. Định nghĩa kiểu giao diện có thể xác định và triển khai các phương thức tĩnh (xem §8.4.3) vì các phương thức tĩnh được liên kết với chính kiểu giao diện chứ không phải với bất kỳ giá trị nào của kiểu.


10
Để tham khảo, CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.Người ta tiếp tục nói rằng người tiêu dùng tuân thủ CLS có thể từ chối các loại giao diện này. Khoảng một năm trước tôi đã thử gọi một phương thức tĩnh trên một giao diện và trình biên dịch C # sẽ không biên dịch nó.
Christopher Currens

Ngoài ra, lưu ý thêm về @ChristopherCurrens về CLS: Common Language Specification (CLS) is a set of basic language features that .Net Languages needed.... When there is a situation to communicate Objects written in different .Net Complaint languages , those objects must expose the features that are common to all the languages. Có nghĩa là nếu CLS nói về khả năng tương tác trên các ngôn ngữ .NET khác nhau và C # không cho phép các thành viên tĩnh trên một giao diện, thì CLS cũng sẽ cấm chúng, để đảm bảo các thư viện trong các ngôn ngữ .NET khác có thể được gọi từ C #.
Simon Tewsi

17

Bạn có thể định nghĩa các phương thức tĩnh trong c # 8 nhưng bạn phải khai báo một phần thân mặc định cho nó.

    public interface IMyInterface
    {
          static string GetHello() =>  "Default Hello from interface" ;
          static void WriteWorld() => Console.WriteLine("Writing World from interface");
    }

hoặc nếu bạn không muốn có bất kỳ nội dung mặc định nào, chỉ cần đặt một ngoại lệ:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }

Có vẻ như các thành viên tĩnh trong giao diện khá vô dụng vì bạn không thể truy cập chúng bằng phiên bản giao diện. Ít nhất trong C # 8.
Pavel Sapehin

3
như một quan điểm triển khai Giao diện, quyền của bạn. Nó vô dụng. nhưng bằng cách này ít nhất bạn chắc chắn có một phương thức được triển khai trên mọi lớp đang sử dụng giao diện này. (đây là loại triển khai tùy chọn cho các giao diện)
AliReza

5

Bạn có thể gọi nó với sự phản chiếu:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);

5
Và nếu bạn không có một phiên bản MyInterface, bạn có thể sử dụng "typeOf (MyInterface)" thay vì "myInterface.GetType ()".
RenniePet

Có vẻ như đó là một ý tưởng hay vào thời điểm đó và tôi có thể tiếp tục làm điều đó thông qua phản ánh, nhưng có một cảnh báo nhỏ: nó sẽ trở nên rắc rối hơn nếu chương trình bị xáo trộn khiến phương thức StaticMethod được đổi tên.
RenniePet 17/10/16

1
@RenniePet: Bạn có thể giải quyết một phần việc đổi tên StaticMethod bằng cách sử dụng nameof (StaticMethod). Nó CÓ THỂ trợ giúp với một obfuscator tùy thuộc vào cách nó đổi tên. Nếu bạn làm theo cách này, ít nhất bạn sẽ thấy lỗi thời gian biên dịch.
Brent Rittenhouse

Reflection là quá cực đoan đối với trường hợp này
Stepan Ivanenko

3

C # "Ten" sẽ cho phép các thành viên tĩnh trên các giao diện , cùng với các vai trò. Đây là một bước tiến vượt bậc, nó cũng sẽ cho phép nạp chồng toán tử chung mà không cần sử dụng phản xạ. Đây là một đoạn mã ví dụ về cách hoạt động của nó, sử dụng ví dụ monoid cổ điển, chỉ là biệt ngữ để nói "thứ gì đó có thể được thêm vào". Lấy trực tiếp từ Mads Torgersen: C # vào tương lai :

interface IMonoid<T>
{
    static T Zero { get; }
    static T operator +(T t1, T t2);
}

public static T AddAll<T>(T[] ts) where T : IMonoid<T>
{
    T result = T.Zero;
    foreach (T t in ts) { result += t; }
    return result;
}

role IntAddMonoid extends int : IMonoid<int>
{
    public static int Zero => 0;
}

IntAddMonoid[] values = new int[] {1, 2, 4, 8, 16, 32};
int sixtyThree = AddAll<IntAddMonoid>(values); // == 63

Các nguồn bổ sung:

Jeremy Bytes: Giao diện C # 8 thành viên tĩnh

BIÊN TẬP

Bài viết này ban đầu nói giao diện thành viên tĩnh sẽ được thêm vào trong C # 8.0 , điều này không đúng, tôi đã hiểu sai lời của Mads Torgersen trong video. Hướng dẫn C # 8.0 chính thức chưa nói về các thành viên giao diện tĩnh, nhưng rõ ràng là họ đã làm việc trên nó từ khá lâu rồi.


1

Về lý do tại sao bạn không thể có một phương thức tĩnh trên một giao diện: Tại sao C # không cho phép các phương thức tĩnh triển khai một giao diện?

Tuy nhiên, tôi khuyên bạn nên loại bỏ các phương thức tĩnh thay vì các phương thức instance. Nếu điều đó là không thể, thì bạn có thể bọc các lệnh gọi phương thức tĩnh bên trong một phương thức cá thể, sau đó bạn có thể tạo một giao diện cho điều đó và chạy các bài kiểm tra đơn vị của bạn từ đó.

I E

public static class MyStaticClass
{
    public static void MyStaticMethod()
    {...}
}

public interface IStaticWrapper
{
    void MyMethod();
}

public class MyClass : IStaticWrapper
{
    public void MyMethod()
    {
        MyStaticClass.MyStaticMethod();
    }
}

lợi thế của việc sử dụng giao diện với lớp tĩnh so với chỉ sử dụng giao diện là gì?
Selen

1

C # 8 cho phép các thành viên tĩnh trên giao diện

Bắt đầu với C # 8.0, một giao diện có thể xác định một cài đặt mặc định cho các thành viên. Nó cũng có thể xác định các thành viên tĩnh để cung cấp một triển khai duy nhất cho chức năng chung.

giao diện (Tham chiếu C #)

Ví dụ

public interface IGetSomething
{
    public static string Something = "something";
}

var something = IGetSomething.Something;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.