C#:接口中的枚举
我已经看到了一些类似于这个问题的线索,但没有一个真正回答了我想问的问题 对于初学者来说,不幸的是,我正在使用现有的API代码,虽然可能有更好的方法来完成我所要求的,但我仍然坚持这样做,因为向后兼容性是不可协商的 我有一个响应类,当前包含错误代码的枚举和字符串描述。错误代码定义了一组相当好且完整的响应,这些响应在语义上都与使用它们的操作相耦合 不幸的是,我现在必须为一组类似的API对象添加一个不同的工作流,这将需要一个字符串描述,这很好,但也需要一个由一组完全不相关的错误代码组成的枚举错误代码。错误代码(以及对象模型的其他方面)将在许多相同的类中使用,因此最好有一个接口,这样我就可以通过相同的框架运行对象 这里的目的是签订一份合同,上面写着“我有一个错误代码,以及该错误代码的描述。” 但是,据我所知,无法将项添加到接口,例如C#:接口中的枚举,c#,generics,interface,enums,C#,Generics,Interface,Enums,我已经看到了一些类似于这个问题的线索,但没有一个真正回答了我想问的问题 对于初学者来说,不幸的是,我正在使用现有的API代码,虽然可能有更好的方法来完成我所要求的,但我仍然坚持这样做,因为向后兼容性是不可协商的 我有一个响应类,当前包含错误代码的枚举和字符串描述。错误代码定义了一组相当好且完整的响应,这些响应在语义上都与使用它们的操作相耦合 不幸的是,我现在必须为一组类似的API对象添加一个不同的工作流,这将需要一个字符串描述,这很好,但也需要一个由一组完全不相关的错误代码组成的枚举错误代码。错
public interface IError
{
enum ErrorCode;
string Description;
}
也没有表达的方式
public interface IError<T> where T: enum
{
T ErrorCode;
string Description;
}
公共接口错误,其中T:enum
{
错误码;
字符串描述;
}
以前有人碰到过这样的事情吗?是的,我碰到过这样的事情。不是在这种特殊情况下,而是在其他堆栈溢出问题中。(我不打算以重复的方式结束这一次,因为它略有不同。) 可以表达您的通用接口—只是不能用C#。你可以在IL中毫无问题地完成它。我希望在C#5中可以消除这个限制。据我所见,C#编译器实际上完全正确地处理了约束 如果您真的想将此作为一个选项,您可以使用类似于中的代码,我有一个库,它公开了各种方法,具有这种难以生成的约束。它有效地使用了IL重写——这很粗糙,但它对嗯有效,而且可能对你也有效。不过,您可能希望将接口放入一个单独的程序集中,这会有点尴尬
当然,您可以让您的界面只包含
T:struct
。。。这并不理想,但至少会在某种程度上限制类型。只要你能确保它没有被滥用,那就相当好了。无法编写公共接口IError,其中t:enum
是我们多年来一直抱怨的问题。到目前为止,在这方面没有任何进展
我通常会编写
公共接口IError
,并给实现者留下一条提示,T必须是一个枚举。如果我理解您想要做什么,那么是的,没有办法定义一个接口,该接口包含一个非特定的枚举作为其成员之一。第二个示例很接近,但您仅限于将T
的类型约束为struct
,这将允许任何值类型。在这一点上,您只需依赖接口的正确使用知识,让人们知道T
的预期类型应该是枚举。通过将T
定义为TEnum
或TErrorValue
,您可能会让它更清晰一些:
public interface IError<TEnum> where T: struct
{
T ErrorCode;
string Description;
}
公共接口错误,其中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
键入IError System.Enum和'T:struct>=
抽象成员错误:“T”
抽象成员描述:字符串
如果将其放在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 class ErrorFlag//基本枚举类
{
内部int值{get;set;}
公共布尔等于(ErrorFlag其他)
{
if(ReferenceEquals(this,other))
返回true;
if(ReferenceEquals(null,其他))
返回false;
返回值==other.value;
}
公共覆盖布尔等于(对象对象对象)
{
返回等于(obj作为ErrorFlag);
}
公共静态布尔运算符==(ErrorFlag lhs、ErrorFlag rhs)
{
if(ReferenceEquals(lhs,null))
返回ReferenceEquals(rhs,null);
返回lhs等于(rhs);
}
公共静态布尔运算符!=(ErrorFlag lhs,ErrorFlag rhs)
{
返回!(lhs==rhs);
}
公共覆盖int GetHashCode()
{
返回值;
}
公共重写字符串ToString()
{
返回值.ToString();
}
}
公共接口IError,其中T:ErrorFlag
{
错误代码{get;set;}
字符串说明{get;set;}
}
//枚举类
公共密封类ReportErrorFlag:ErrorFlag
{
//基本上你的枚举值
public static readonly ReportErrorFlag Report1=new ReportErrorFlag{value=1};
公共静态只读Repor