Як я можу реалізувати статичні методи на інтерфейсі?


92

У мене є стороння бібліотека C ++ DLL, яку я телефоную з C #.

Методи статичні.

Я хочу абстрагувати це, щоб виконати певне модульне тестування, тому я створив інтерфейс зі статичними методами в ньому, але тепер мої помилки програми:

Модифікатор 'static' недійсний для цього елемента

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

Як я можу досягти цієї абстракції?

Мій код виглядає так

private IInterfaceWithStaticMethods MyInterface;

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

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

3
Може бути , ви можете зробити це з допомогою методів розширення: stackoverflow.com/questions/1243921 / ...
HCB

Відповіді:


47

Ви не можете визначити статичні члени інтерфейсу в C #. Інтерфейс - це контракт для екземплярів .

Я б порекомендував створити такий інтерфейс, як ви зараз, але без ключового слова static. Потім створіть клас, StaticIInterfaceякий реалізує інтерфейс і викликає статичні методи C ++. Щоб виконати модульне тестування, створіть інший клас FakeIInterface, який також реалізує інтерфейс, але робить те, що потрібно для обробки ваших модульних тестів.

Після того, як ви визначили ці 2 класи, ви можете створити той, який вам потрібен для вашого середовища, і передати його MyClassконструктору.


65
-1 для висловлювання An interface is a contract, not an implementation.- це правда, але тут абсолютно не має значення ( non sequitur ), оскільки статичний метод не є частиною самої реалізації - реалізація, за визначенням, базується на даних , які, в свою чергу, недоступні для статичних членів. 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.- майте на увазі, що staticучасники, як правило, є корисними методами .

3
Я розумію і погоджуюся з Вашими твердженнями, і я вважаю, що Ваш коментар також є важливим контекстом. Хоча. При розробці інтерфейсу слід думати про це як про контракт, що означає, що статичні методи не застосовуються. Я вирішив, що повинен залишити це там, щоб допомогти деяким людям зрозуміти призначення інтерфейсу. Чи відчуває громада, що її слід видалити?
davisoa

1
Я частково погоджуюсь, що An interface is a contract, not an implementationмарно, іноді трохи контекстуалізація дійсно допомагає. І я цілком погоджуюсь із тим static method is not a part of implementation itself , що статичні методи мають реалізацію, вони стають частиною реалізації лише за умови використання як реалізації у реалізації іншого методу. Однак мій словник базується на вивченому, наскільки я знаю, термінологія дійсно варіюється залежно від мови програмування. Статичні методи не можуть бути інтерфейсами, оскільки в будь-якому випадку може бути лише 1 реалізація.
CoffeDeveloper

Уявіть, у мене є IPersonконтракт, в якому це зазначеноGetCountry буде ім'я країни походження людини ... FrenchPersonвсі організації будуть говорити "Франція" і GermanPersonвсі будуть говорити "Німеччина", також корисно, коли різні типи організацій мають однакову таблицю (дані), як MS Azure один, скажімо Connection, Postі Commentзберігаються в UsersAzureTable, тому об'єкти дерева мають загальну інформацію, IUsersможе мати GetTableNameстатичний метод ...
Serge

@vaxquis - IMHO, "його контракт" буде доречним, якщо речення буде переформульовано: `Інтерфейс - це контракт для екземплярів . Статичні члени є частиною типу; це переформульоване речення говорить (правильно), що вони не мають значення в інстанційному договорі. Тому я думаю, що проблема полягає лише у неточних формулюваннях, а не в не послідовності.
ToolmakerSteve

112

Інтерфейси не можуть мати статичних членів, а статичні методи не можуть використовуватися як реалізація методів інтерфейсу.

Що ви можете зробити, це використовувати явну реалізацію інтерфейсу:

public interface IMyInterface
{
    void MyMethod();
}

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

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

Крім того, ви можете просто використовувати нестатичні методи, навіть якщо вони не мають доступу до жодних конкретних примірників.


18
Для тих, хто задається питанням, чому хтось хоче це зробити, це особливо корисно при написанні модульних / інтеграційних тестів для застарілого коду, що реалізує статичні методи.
Dezzamondo

Ця техніка дуже добре працювала для реалізації швидкого RESTful API, який потребував збереження даних, але не міг використовувати базу даних. Реалізація працювала лише з об'єктами C # в пам'яті, тому не було місця для зберігання даних, але використання статичного властивості полегшило потребу в базі даних в пам'яті за допомогою EF Core або SQLite.
програмне забезпечення

19

Статичні члени абсолютно законні в CLR, тільки не C #.

Ви можете застосувати трохи клею в IL, щоб зв’язати деталі реалізації.

Не впевнені, чи зможе компілятор C # дозволити їх викликати?

Див .: 8.9.4 Визначення типу інтерфейсу ECMA-335.

Типи інтерфейсів неодмінно неповні, оскільки вони нічого не говорять про представлення значень типу інтерфейсу. З цієї причини визначення типу інтерфейсу не повинно надавати визначень полів для значень типу інтерфейсу (тобто полів екземпляра), хоча воно може оголошувати статичні поля (див. §8.4.3).

Аналогічним чином, визначення типу інтерфейсу не повинно забезпечувати реалізації будь-яких методів на значеннях свого типу. Однак визначення типу інтерфейсу може - і зазвичай робить - визначати контракти методів (ім'я методу та підпис методу), які повинні бути реалізовані за допомогою підтримуючих типів. Визначення типу інтерфейсу може визначати та реалізовувати статичні методи (див. П. 8.4.3), оскільки статичні методи пов'язані з самим типом інтерфейсу, а не з будь-яким значенням типу.


10
Для довідки CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.Далі йдеться про те, що споживачам, що відповідають CLS, нормально відмовлятися від таких типів інтерфейсів. Я намагався близько року тому викликати статичний метод в інтерфейсі, і компілятор C # не збирався його компілювати.
Крістофер Керренс,

Далі до примітки @ChristopherCurrens про 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. має сенс, що якщо CLS стосується сумісності між різними мовами .NET, а C # не дозволяє статичним членам інтерфейсу, то CLS забороняє їх також, щоб забезпечити бібліотеки в інші мови .NET можна викликати з C #.
Саймон Тевсі

17

Ви можете визначити статичні методи в c # 8, але для цього потрібно оголосити тіло за замовчуванням.

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

або якщо ви не хочете мати тіло за замовчуванням, просто викиньте виняток:

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

Здається, статичні члени в інтерфейсах досить марні, оскільки ви не можете отримати до них доступ за допомогою екземпляра інтерфейсу. Принаймні в С # 8.
Павло Сапегін

3
як точки зору реалізації інтерфейсу, ваше право. це марно. але таким чином, принаймні, ви впевнені, що маєте реалізований метод для кожного класу, який використовує цей інтерфейс. (це свого роду необов’язкова реалізація для інтерфейсів)
AliReza

5

Ви можете викликати це з роздумом:

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

5
І якщо у вас немає екземпляра MyInterface, ви можете використовувати "typeOf (MyInterface)" замість "myInterface.GetType ()".
RenniePet

У той час це здавалося гарною ідеєю, і я можу продовжувати робити це за допомогою роздумів, але одне невелике застереження: це стає більш проблематичним, якщо програма стає заплутаною так, що метод StaticMethod перейменовується.
RenniePet,

1
@RenniePet: Ви могли частково мати справу з перейменуванням StaticMethod, використовуючи замість цього nameof (StaticMethod). Це МОЖЕ допомогти обфускатору залежно від того, як він перейменований. Якщо ви робите це таким чином, ви хоч би побачили помилку часу компіляції.
Brent Rittenhouse

Роздуми надто екстремальні для цієї справи
Степан Іваненко

3

C # "Десять" дозволить статичним членам на інтерфейсах , поряд з ролями. Це величезний крок вперед, він також дозволить перевантажити загальний оператор, не використовуючи жодного відображення. Ось приклад фрагмента, як це працює, з використанням класичного прикладу моноїдів, який є просто жаргоном для того, щоб сказати "те, що можна додати". Взято безпосередньо від Мадса Торгерсена: C # у майбутнє :

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 # 8

РЕДАГУВАТИ

Цей спочатку заявлений статичний член інтерфейсу буде доданий до C # 8.0 , що не відповідає дійсності, я неправильно інтерпретував слова Мадса Торгерсена у відео. В офіційному посібнику C # 8.0 ще не йдеться про статичних членів інтерфейсу, але зрозуміло, що вони працюють над цим вже досить довго.


1

Щодо того, чому у вас не може бути статичного методу на інтерфейсі: Чому C # не дозволяє статичним методам реалізовувати інтерфейс?

Однак я б запропонував видалити статичні методи на користь методів екземпляра. Якщо це неможливо, тоді ви можете обернути виклики статичного методу всередині методу екземпляра, а потім ви можете створити для цього інтерфейс і запустити з цього свої модульні тести.

тобто

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

public interface IStaticWrapper
{
    void MyMethod();
}

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

яка перевага використання інтерфейсу зі статичним класом перед використанням лише інтерфейсу?
Селен

1

C # 8 Дозволяє статичним членам інтерфейсів

Починаючи з C # 8.0, інтерфейс може визначати реалізацію за замовчуванням для членів. Він також може визначати статичні члени, щоб забезпечити єдину реалізацію для загальної функціональності.

інтерфейс (посилання на C #)

Напр

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

var something = IGetSomething.Something;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.