簡體   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