繁体   English   中英

C#:接口中的枚举

[英]C#: Enums in Interfaces

我已经看到了几个与此问题类似的线索,但他们都没有真正回答我想问的问题。

对于初学者来说,不幸的是我正在使用现有的API代码,所以很遗憾,虽然可能有更好的方法来做我要求的事情,但我仍然坚持这样做,因为向后兼容性是非-negotiable。

我有一个响应类,当前包含一个错误代码和字符串描述的枚举。 错误代码定义了一组相当不错且完整的响应,这些响应在语义上与使用它们的操作非常相似。

不幸的是,我现在必须为一组类似的API对象添加不同的工作流程,这将需要一个字符串描述,这很好,但也是一个包含一组完全不相关的错误代码的枚举错误代码。 错误代码(以及对象模型的其他方面)将在许多相同的类中使用,因此最好让接口运行以便我可以通过相同的框架运行对象。

这里的目的是签订一份合同,上面写着“我有一个错误代码,以及该错误代码的描述”。

但是,据我所知,没有办法将项目添加到界面,如

public interface IError
{
    enum ErrorCode;
    string Description;
}

也没有办法表达

public interface IError<T> where T: enum
{
    T ErrorCode;
    string Description;
}

以前每个人都遇到这样的事情吗?

是的,我遇到了这个问题。 不是在这种特殊情况下,而是在其他Stack Overflow问题中, 就像这个问题一样 (我不会投票将这个作为副本关闭,因为它略有不同。)

可以表达自己的通用接口-只是没有在C#。 你可以在IL中做到没有问题。 我希望在C#5中可以删除限制。就我所见,C#编译器实际上正确地处理了约束。

如果你真的想要选择这个,你可以使用类似于Unconstrained Melody中的代码,这是一个我已经得到的库,它使用这种难以生成的约束来暴露各种方法。 它有效地使用IL重写 - 它很粗糙,但它适用于UM,也可能适合你。 你可能想把接口放到一个单独的程序集中,这有点尴尬。

当然,你可以让你的界面只有T : struct而不是......它不是理想的,但它至少会在某种程度上限制这种类型。 只要你能确保它没有被滥用,那就行得相当好。

正如Jon Skeet所提到的,基本IL支持将泛型约束为枚举,但C#不允许您利用它。

但是,F#确实允许这种约束。 此外,如果接口是在F#中定义的,则约束将在实现接口的C#代码中强制执行。 如果你愿意在你的解决方案中混合使用语言,那么这样的话应该可以正常工作:

type IError<'T when 'T :> System.Enum and 'T : struct> =
    abstract member Error : 'T
    abstract member Description : string

如果将其放在F#项目中并从C#项目引用它,那么实现该接口的C#代码将导致C#编译器错误,因为任何尝试将其与非枚举类型一起使用。

您可以采用稍微不同的方式使用您的方法:

public interface IError
{
    Enum ErrorCode;
    string Description;
}

System.Enum是你所有枚举的基类,所以它应该处理它,但它远没有表现力。

这里正确的方法是为它构建自己的枚举类和基本枚举类。 例如,

public class ErrorFlag // base enum class
{
    int value;

    ErrorFlag() 
    {

    }

    public static implicit operator ErrorFlag(int i)
    {
        return new ErrorFlag { value = i };
    }

    public bool Equals(ErrorFlag other)
    {
        if (ReferenceEquals(this, other))
            return true;

        if (ReferenceEquals(null, other))
            return false;

        return value == other.value;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as ErrorFlag);
    }

    public static bool operator ==(ErrorFlag lhs, ErrorFlag rhs)
    {
        if (ReferenceEquals(lhs, null))
            return ReferenceEquals(rhs, null);

        return lhs.Equals(rhs);
    }

    public static bool operator !=(ErrorFlag lhs, ErrorFlag rhs)
    {
        return !(lhs == rhs);
    }

    public override int GetHashCode()
    {
        return value;
    }

    public override string ToString()
    {
        return value.ToString();
    }
}

public interface IError
{
    ErrorFlag ErrorCode;
    string Description;
}

现在,不要使用自己的错误枚举,而是编写自己的ErrorFlag类。

public sealed class ReportErrorFlag : ErrorFlag
{
    //basically your enum values
    public static readonly ErrorFlag Report1 = 1;
    public static readonly ErrorFlag Report2 = 2;
    public static readonly ErrorFlag Report3 = 3;

    ReportErrorFlag() 
    {

    }
}

public sealed class DataErrorFlag : ErrorFlag
{
    //basically your enum values
    public static readonly ErrorFlag Data1 = 1;
    public static readonly ErrorFlag Data2 = 2;
    public static readonly ErrorFlag Data3 = 3;

    DataErrorFlag() 
    {

    }
}

// etc

现在你的主要课程:

public class ReportError : IError
{
    // implementation
}

public class DataError : IError
{
    // implementation
}

或者,否则,

public class ErrorFlag // base enum class
{
    internal int value { get; set; }

    public bool Equals(ErrorFlag other)
    {
        if (ReferenceEquals(this, other))
            return true;

        if (ReferenceEquals(null, other))
            return false;

        return value == other.value;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as ErrorFlag);
    }

    public static bool operator ==(ErrorFlag lhs, ErrorFlag rhs)
    {
        if (ReferenceEquals(lhs, null))
            return ReferenceEquals(rhs, null);

        return lhs.Equals(rhs);
    }

    public static bool operator !=(ErrorFlag lhs, ErrorFlag rhs)
    {
        return !(lhs == rhs);
    }

    public override int GetHashCode()
    {
        return value;
    }

    public override string ToString()
    {
        return value.ToString();
    }
}

public interface IError<T> where T : ErrorFlag
{
    T ErrorCode { get; set; }
    string Description { get; set; }
}

//enum classes
public sealed class ReportErrorFlag : ErrorFlag
{
    //basically your enum values
    public static readonly ReportErrorFlag Report1 = new ReportErrorFlag { value = 1 };
    public static readonly ReportErrorFlag Report2 = new ReportErrorFlag { value = 2 };
    public static readonly ReportErrorFlag Report3 = new ReportErrorFlag { value = 3 };

    ReportErrorFlag()
    {

    }
}

public sealed class DataErrorFlag : ErrorFlag
{
    //basically your enum values
    public static readonly DataErrorFlag Data1 = new DataErrorFlag { value = 1 };
    public static readonly DataErrorFlag Data2 = new DataErrorFlag { value = 2 };
    public static readonly DataErrorFlag Data3 = new DataErrorFlag { value = 3 };

    DataErrorFlag()
    {

    }
}

//implement the rest

要有丑陋的枚举约束方式,请参阅任何人都知道缺少枚举通用约束的好方法吗?

无法编写public interface IError<T> where T: enum是我们多年来一直抱怨的东西。 到目前为止,没有任何进展。

我通常最终编写public interface IError<T>并为实现者留下一个注释,即T必须是枚举。

如果我理解你想要做什么,那么是的,没有办法定义一个接口,它包含一个非特定枚举的成员之一。 您的第二个示例是关闭的,但您只能将T的类型约束为struct ,这将允许任何值类型。 那时,您只需要依赖接口正确使用的知识,让人们知道T的预期类型应该是枚举。 通过将T定义为TEnumTErrorValue您可以使其更清晰:

public interface IError<TEnum> where T: struct
{
    T ErrorCode;
    string Description;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM