[英]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
定義為TEnum
或TErrorValue
您可以使其更清晰:
public interface IError<TEnum> where T: struct
{
T ErrorCode;
string Description;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.