[英]C# - switch in generics
我试图在泛型方法中的开关上比较泛型类型参数。 这对我的解决方案不起作用。 原因是:参数必须是特定类型(bool,char,string,integral,enum)。
public T testfunction<T, U>(U numb)
{
switch(numb){ //<-- error
}
....
}
但它背后的意义是什么? 如果参数是通用的并且我想进行比较,为什么它必须是类型定义的变量?
你想在switch语句中测试什么? 当然你必须知道有关正在进入的物体类型的东西。
考虑一下:当您可以在方法中接受Product或Customer类型时,如何构造switch语句? 您希望编译器为您做出的逻辑选择是什么? 如果希望编译器根据产品价格选择操作,则不适用于Customer对象。 但是,如果产品和客户都有您想要转向的CreateDate字段,则可以将其提取到接口中,并将其用作方法的通用约束。
为您的通用方法签名添加适当的约束,该签名封装了您对所期望的类型的了解,然后您将能够切换:
public interface ICreateDate {
public DateTime CreateDate { get; set; }
}
public T testfunction<T, U>(U numb) where U : ICreateDate
{
switch(numb.CreateDate.DayOfWeek){
case DayOfWeek.Monday:
}
....
}
这不起作用,因为switch内部的case
部分需要是特定类型的编译时常量。 例如,你不能做case 1:
因为numb
可能是一个字符串; 你也不能做case "foo":
因为numb
可能是一个整数。 必须在编译时知道numb
的类型才能将其用作切换变量,因为编译器需要知道在case
部分中哪种常量值有效。
这背后的逻辑是switch语句不适用于所有类型,因此它不会让你打开泛型类型。
实际上它适用于很少的类型,int,char,string,bool(在这一个上不确定),也许还有一些更原始的类型。
原始类型是内置于语言中的类型,基本上所有这些都不是类/结构/联合等...
正如许多其他人所说,switch语句必须是编译时常量,如C#语言规范(在此处: http : //www.microsoft.com/en-us/download/details.aspx?id = 7029 )中所述。 8.7.2:
switch语句由关键字switch,后跟括号表达式(称为switch表达式),后跟switch-block组成。 开关组由零个或多个开关部分组成,用括号括起来。 每个switch-section包含一个或多个switch-labels,后跟一个statement-list(第8.2.1节)。 switch语句的控制类型由switch表达式建立。
•如果switch表达式的类型是sbyte,byte,short,ushort,int,uint,long,ulong,bool,char,string或enum-type,或者它是与这些类型之一对应的可空类型那么这就是switch语句的管理类型。
•否则,从switch表达式的类型到以下可能的控制类型之一,必须存在一个用户定义的隐式转换(第6.4节):sbyte,byte,short,ushort,int,uint,long,ulong,char, string,或者,与这些类型之一对应的可空类型。
•否则,如果不存在此类隐式转换,或者存在多个此类隐式转换,则会发生编译时错误。
每个case标签的常量表达式必须表示一个可隐式转换(§6.1)的值到switch语句的控制类型。 如果同一switch语句中的两个或多个case标签指定相同的常量值,则会发生编译时错误。
考虑到这一点,即使你能实现这一点,它也会充满代码味道。 这样做的主要原因是它违反了开放/封闭原则( http://www.oodesign.com/open-close-principle.html ),因为你必须修改这部分代码作为新的类型U(无论它们是什么)被引入。 考虑这个不好的例子:
public enum ItemType : int {
Default = 0,
Basic = 1,
Advanced = 2,
Expert = 3
}
public class NotSoGoodItem {
public string Name { get; set; }
public int Id { get; set; }
public ItemType DataType { get; set; }
public List<String> BasicSettings {get; set;}
public List<String> AdvancedSettings {get; set;}
public List<String> ExperSettings {get; set;}
}
public static NotSoGoodItem CreateNewItem(ItemType item) {
//item is inherently an int
switch (item) {
case ItemType.Default | ItemType.Basic:
return new NotSoGoodItem() { Name = "Basic Item", BasicSettings = new List<String>() };
case ItemType.Advanced:
return new NotSoGoodItem() { Name = "Advanced Item", AdvancedSettings = new List<String>() };
case ItemType.Expert:
return new NotSoGoodItem() { Name = "Expert Item", AdvancedSettings = new List<String>() };
default:
return null;
}
}
在这种情况下,switch语句使用枚举值(继承自int并满足规范要求)来确定要填充的属性以及要设置的值。 这是有问题的,因为如果你想引入一个新的级别或ItemType
,比如Guru,你将不得不修改至少3个代码文件来进行这个改变(enum, NotSoGoodItem
来添加属性,以及执行创建)。
相反,通过使用泛型,可以使用以下良好示例简化此代码:
public abstract class GoodItem {
protected GoodItem() { }
private string _name;
public virtual string Name { get { return string.Concat(Prefix, _name); } set { _name = value; } }
protected virtual string Prefix { get; set; }
public virtual int Id { get; set; }
public virtual List<String> Settings { get; set; }
}
public class BasicItem : GoodItem {
public BasicItem()
: base() {
Prefix = "Basic";
}
}
public class AdvancedItem : GoodItem {
public AdvancedItem()
: base() {
Prefix = "Advanced";
}
}
public class ExpertItem : GoodItem {
public ExpertItem()
: base() {
Prefix = "Expert";
}
}
public static T CreateNewItem<T>() where T : GoodItem {
return Activator.CreateInstance<T>();
}
使用此方法,必须修改零代码文件。 所有需要做的就是添加一个继承自GoodItem
的新类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.