如何将常量与 C# 中的接口相关联?

Posted

技术标签:

【中文标题】如何将常量与 C# 中的接口相关联?【英文标题】:How to associate constants with an interface in C#? 【发布时间】:2012-09-26 23:17:44 【问题描述】:

某些语言允许您将常量与接口相关联:

A Java example A php example

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# 的常用方法是什么? 是否有针对 DOM 接口的“规范”C# 绑定? 虽然 C# 不能,但是否有另一种 .NET 语言可以定义与接口关联的常量?

【问题讨论】:

我认为枚举是您正在寻找的。​​span> 更多信息请见:***.com/questions/2175734/…。 @Oded 枚举必须有名字,不能和接口的名字相同,不能在接口内部定义。 (仅)知道 IDL 接口的客户端代码不会知道人工 C# 枚举的名称。相反,应该有与接口关联的常量整数值。 【参考方案1】:

我添加了一个 Get only 属性并在定义中使用 const 对其进行备份。

public interface IFoo

    string ConstValue  get; 


public class Foo : IFoo

    public string ConstValue => _constValue;
    private string _constValue = "My constant";

【讨论】:

【参考方案2】:

回答你的第三个问题:

虽然 C# 不能,但是否有另一种 .NET 语言可以定义与接口关联的常量?

C++/CLI 允许您在接口中定义 literal 值,这相当于 C# 中的 static const 值。

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);

更多详情请见this page on MSDN。

免责声明: 不允许在接口中使用常量值的设计决定是一个很好的决定。暴露实现细节的接口很可能是一个泄漏的抽象。在此示例中,CSS 值类型可能最好是枚举。

【讨论】:

虽然我很欣赏 CLR 允许您这样做,但用 C++ 编写接口只是为了能够在其中包含常量似乎违反直觉。不过没关系。 @kprobst 我不同意你的观点 :)【参考方案3】:

如果你想要一个地方来存储你的常量,我会使用一个静态类:

public static class MyConstants

    public const int first = 1;
    public const int second = 2;
    public const string projectName = "Hello World";

这是(至少一个)通用标准。

【讨论】:

客户端需要知道人工静态类的名称(因为他们需要知道人工枚举类型的名称)。该静态类的名称是否有任何“通用标准”? @ChrisW 就像他们需要知道人工界面的名称一样……没有什么不同。【参考方案4】:

C# 不允许在接口中使用常量,因为常量是一个实现方面,理论上不属于仅定义行为协议的类型。

我怀疑 Java 人员允许在接口中使用 const 字段是因为接口在内部被视为某种抽象类,或者是因为他们需要它来弥补类型系统中的某些缺陷,例如枚举。

我不确定您所说的“DOM 接口的规范绑定”是什么意思。 C# 不在浏览器中运行。

也就是说,您需要将常量放在其他位置,例如结构或枚举(如果它们是数字的话)。也许遵循某种命名约定会有所帮助——如果你的接口是IFooBar,那么可能包含你的常量的结构可以称为IFooSetttingsIFooValues 或任何合适的名称。

除了 C# 和 VB.NET,我不知道任何 CLR 语言,但我很确定 VB 不允许这样做(尽管已经有一段时间了)。

【讨论】:

+1 CLR 确实允许在接口中使用常量值,但 AFAIK 没有一种主流语言在设计上支持这一点。 Java enums 比 C# enums 更强大。事实上,它们是我在 Java 中唯一喜欢的东西。答案是因为它们在幕后被视为纯粹的abstract class。 DOM 接口由 W3C 作为 IDL 发布,以便新的浏览器可以滚动他们自己的实现,同时仍然符合标准。 很好的答案,尤其是关于枚举的部分。由于缺少枚举(直到 1.5),Java 中接口中的常量几乎被普遍使用。 @MattDavey 你是说它们可以通过发出 IL 来定义吗?它们会被定义为静态字段吗?如果这样做了,那么像 C# 这样的主流语言是否可以引用这样的字段? @ChrisW 理论上是的。经过调查,我发现您可以在 C++/CLI 的接口中定义文字值。public interface class IExample public: literal float Pi = 3.14159f 相当于 static const 变量。【参考方案5】:

我一直使用 SO,但这是我第一次在这里发帖。我找到了这篇文章,试图解决同样的问题。当我看到关于使用静态类的帖子(由 Servy 撰写)时,我开始考虑通过在静态类中嵌入接口来解决这个问题。

// 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

【讨论】:

【参考方案6】:

C# 8 现在允许在 interface 定义中使用常量。

【讨论】:

怎么样?我正在使用 C# 8,public const type name = value 语法因“目标运行时不支持默认接口实现”而导致编译失败。 @glen-little 您能否包含指向支持您声明的 C# 文档的链接。我找不到任何 interface doc【参考方案7】:

一个抽象类会做接口会做的所有事情(嗯,除了通过typeof(T).IsInterface 测试)并允许使用常量。

反对嵌入在接口中的常量(或枚举)是错误的。这是一个命名问题。在它们有意义的非常精确的上下文中命名常量比在上下文中命名它们更好。

【讨论】:

多继承怎么样? 我同意你的观点:常量应该在它们打算使用的上下文中。常量值不是逻辑或行为,因此不是实现。这只是一个值。这段对话证明了一种语言的人为约束。【参考方案8】:

遇到同样的问题 - 需要在接口中定义常量。 发现了另一种可能性:通过在接口上使用扩展方法。 像这样:

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();
    
  

【讨论】:

【参考方案9】:

尝试定义方法参数和/或返回值

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();

【讨论】:

【参考方案10】:

可以使用自定义属性:

[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);

【讨论】:

以上是关于如何将常量与 C# 中的接口相关联?的主要内容,如果未能解决你的问题,请参考以下文章

类和对象

如何将我的用户的支出与他们在 Firebase 中的个人资料相关联?

如何使用 RuntimeTypeModel 将 ProtoInclude 与 protobuf-net 中的类型相关联?

如何将用户登录与表单中的模型相关联?

如何将 PayPal 交易与我数据库中的会员帐户信息相关联?

如何将过滤器与 Google App Engine 的 app.yaml 中的 servlet 相关联?