Как связать константы с интерфейсом в C#?

Некоторые языки позволяют ассоциировать константу с интерфейсом:

Абстрактные интерфейсы W3C делают то же самое, например:

// Introduced in DOM Level 2:
interface CSSValue {

  // UnitTypes
  const unsigned short      CSS_INHERIT                    = 0;
  const unsigned short      CSS_PRIMITIVE_VALUE            = 1;
  const unsigned short      CSS_VALUE_LIST                 = 2;
  const unsigned short      CSS_CUSTOM                     = 3;

  attribute DOMString       cssText;
  attribute unsigned short  cssValueType;
};

Я хочу определить этот интерфейс так, чтобы он мог быть вызван из C#.

Очевидно C# не может определить константу, связанную с интерфейсом.

  • Как обычно переводить такой интерфейс на C#?
  • Существуют ли какие-либо "канонические" привязки C# для интерфейсов DOM?
  • Хотя C# не может, есть ли другой язык.NET, который может определять константы, связанные с интерфейсом?

10 ответов

Решение

C# 8 теперь допускает константы в interface определение.

Я добавил свойство Get only и добавил в определение значение const.

public interface IFoo
{
    string ConstValue { get; }
}

public class Foo : IFoo
{
    public string ConstValue => _constValue;
    private string _constValue = "My constant";
}

Чтобы ответить на ваш третий вопрос:

Хотя C# не может, есть ли другой язык.NET, который может определять константы, связанные с интерфейсом?

C++ / CLI позволяет вам определять literal значения в интерфейсе, которые эквивалентны static const значения в C#.

public interface class ICSSValue
{
public:
    literal short CSS_INHERIT = 0;
    literal short CSS_PRIMITIVE_VALUE = 1;
    literal short CSS_VALUE_LIST = 2;
    literal short CSS_CSS_CUSTOM = 3;

    property DOMString^ cssText;
    property ushort cssValueType;
}

Затем вы можете получить доступ к значениям через C#:

public static void Main()
{
    short primitiveValue = ICSSValue.CSS_PRIMITIVE_VALUE;

    Debug.Assert(primitiveValue == 1);
}

Смотрите эту страницу на MSDN для более подробной информации.

Отказ от ответственности: проектное решение запретить постоянные значения в интерфейсах было хорошим. Интерфейс, который предоставляет детали реализации, скорее всего, является утечкой абстракции. В этом примере CSS Value Type, вероятно, лучше быть перечислением.

Если вы хотите место для хранения ваших констант, я бы использовал статический класс:

public static class MyConstants
{
    public const int first = 1;
    public const int second = 2;
    public const string projectName = "Hello World";
}

Это (по крайней мере, один) общий стандарт.

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

Я подозреваю, что люди Java допускают использование константных полей в интерфейсах либо потому, что интерфейс обрабатывается внутренне, как некоторый абстрактный класс, либо потому, что им это нужно, чтобы восполнить некоторый недостаток в системе типов, например, перечисления.

Я не уверен, что вы подразумеваете под "каноническими привязками для интерфейсов DOM". C# не работает в браузере.

Тем не менее, вам нужно поместить ваши константы в другое место, например, в struct или enum (если они числовые). Возможно, следование некоторому соглашению об именах поможет - если ваш интерфейс IFooBar тогда, возможно, структуру, которая содержит вашу константу, можно было бы назвать IFooSetttings или же IFooValues или что там уместно.

Я не знаю языков CLR, кроме C# и VB.NET, но я почти уверен, что VB этого не допускает (хотя это было давно).

Я использую ТАК все время, но это мой первый пост здесь. Я нашел этот пост, пытаясь решить ту же проблему. Когда я увидел сообщение об использовании статического класса (от Servy), я подумал о том, чтобы решить эту проблему, внедрив Interface в этот статический класс.

// define an interface with static content
public static class X {
    // define the interface to implement
    public interface Interface {
        string GetX();
    }

    // static content available to all implementers of this interface
    public static string StandardFormat(object x) {
        return string.Format("Object = {0}", x.ToString());
    }
}

// Implement the interface
public class MyX : X.Interface {
    public override string ToString() {
        return "MyX";
    }

    #region X.Interface Members

    public string GetX() {
        // use common code defined in the "interface"
        return X.StandardFormat(this);
    }

    #endregion
}

Возникла та же проблема - нужно определить константы в интерфейсе. Нашел еще одну возможность: используя методы расширения в интерфейсе. Как это:

public interface IFoo
{
    // declarations
}
 
public static class IFooExtensions
{
    public static int Bar(this IFoo x) => 50;
}

Однако для этого потребуется экземпляр интерфейса, чтобы получить от него константу:

IFoo foo;
foo.Bar();

И вы также можете использовать эту константу в реализациях интерфейса:

public class Foo: IFoo
{
    public Foo() {
        var x = this.Bar();
    }
}  

Абстрактный класс будет делать все, что будет делать интерфейс (ну, кроме передачи typeof(T).IsInterface тест) и разрешить константы.

Возражение против констант (или перечислений), встроенных в интерфейсы, неуместно. Это проблема с именами. Называть константы в очень точном контексте, где они имеют смысл, лучше, чем называть их вне контекста.

Пользовательский атрибут может быть использован:

[Constant("CSS_INHERIT", 0)]
[Constant("CSS_PRIMITIVE_VALUE", 1)]
public interface BlaBla

Класс пользовательских атрибутов может выглядеть так:

[AttributeUsage(AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
public class ConstantAttribute: Attribute
{
    public ConstantAttribute(string key, object value)
    {
        // ...
    }
}

Константы могут быть получены с помощью

object[] attributes = typeof(BlaBla).GetCustomAttributes(typeof(ConstantAttribute),
    inherit: false);

Попробуй определить это с помощью параметров метода и / или возвращаемых значений

public enum IFIleValue
{
    F_OK = 0,
    F_WRONG_NAME = -1,
    F_ERROR_OBJECT_DATA = -2,
};

public interface IFile
{
     IFIleValue New(String Name=null);
     IFIleValue Open(String Path);
     IFIleValue Save();
     IFIleValue SaveAs(String Path);
     IFIleValue Close();
}
Другие вопросы по тегам