没有现有问题可以回答这个问题。
在c#7中,我可以直接切换到System.Type
吗?
当我尝试:
switch (Type)
{
case typeof(int):
break;
}
它告诉我typeof(int)
需要是一个常量表达式。
是否有一些合成糖可以让我避免case nameof(int):
并直接比较相等的类型?案例语句中的nameof(T)
并不完全正确,因为名称空间。因此,虽然名称冲突可能不适用于int
,但它适用于其他比较。
换句话说,我试图比这更直接:
switch (Type.Name)
{
case nameof(Int32):
case nameof(Decimal):
this.value = Math.Max(Math.Min(0, Maximum), Minimum); // enforce minimum
break;
}
答案 0 :(得分:53)
(已经链接的)新模式匹配功能允许这样做。
通常,你打开一个值:
switch (this.value) {
case int intValue:
this.value = Math.Max(Math.Min(intValue, Maximum), Minimum);
break;
case decimal decimalValue:
this.value = Math.Max(Math.Min(decimalValue, Maximum), Minimum);
break;
}
但是你可以用它来打开一个类型,如果你只有一个类型:
switch (type) {
case Type intType when intType == typeof(int):
case Type decimalType when decimalType == typeof(decimal):
this.value = Math.Max(Math.Min(this.value, Maximum), Minimum);
break;
}
请注意,这不是该功能的用途,它的可读性低于传统的if
... else if
... else if
... {{1}链,而传统的链条无论如何都要编译。我不建议像这样使用模式匹配。
答案 1 :(得分:9)
从Paulustrious的想法开始,即开启一个常数,但争取最可读性:
Type type = GetMyType();
switch (true)
{
case bool _ when type == typeof(int):
break;
case bool _ when type == typeof(double):
break;
case bool _ when type == typeof(string):
break;
default:
break;
}
什么是可读的是主观的。很久以前我曾经在VB中做类似的事情,所以我习惯了这种形式(但是在VB中不需要bool _
所以它不存在)。不幸的是,在c#中需要bool _
。我正在使用c#7.0,我认为在早期的编译器中可能不支持切换常量,但我不确定,所以如果你愿意,可以试试。我认为S / O代码格式化程序还不了解when
是有趣的。
如果您需要case
变量,那么您当然不希望这样做,就像子类一样。
但对于任意布尔表达式,它更适合,例如:
switch (true)
{
case bool _ when extruder.Temperature < 200:
HeatUpExtruder();
break;
case bool _ when bed.Temperature < 60:
HeatUpBed();
break;
case bool _ when bed.Y < 0 || bed.Y > 300:
HomeYAxis();
break;
default:
StartPrintJob();
break;
}
有些人会认为这比if..else更糟糕。我唯一可以说的是switch
迫使一条路径,并且不可能打破switch
语句本身,但是可以省略else
并打破if..else到多个语句无意中,可能意外地执行了两个“分支”。
启用Type
实际上只是一个任意切换,因为我们真正切换的是变量的属性。除非我们能够case typeof(int)
(case
处理不是常量表达式的东西),否则如果我们不想使用字符串常量,我们就会遇到类似于此的东西,在这种情况下类型名称,不在框架中。
答案 2 :(得分:4)
OP在此处提出的问题是,当您没有实际的 实例时,您将无法使用基于 C#7 的新类型的开关功能 可用,但您只能使用其推定的System.Type
。总结如下的accepted answer可以很好地用于精确的类型匹配(此处显示了较小的改进,但是请参见下面的最终示例,以进行进一步的精简)...
Type type = ...
switch (type)
{
case Type _ when type == typeof(Int32):
case Type _ when type == typeof(Decimal):
this.value = Math.Max(Math.Min(this.value, Maximum), Minimum);
break;
}
...但是要特别注意的是,对于派生的引用类型层次结构,这将 不 表现出与使用{的if... else
链相同的行为{1}}个关键字以进行匹配。考虑:
is
由于class TBase { }
class TDerived1 : TBase { }
class TDerived2 : TBase { }
class TDerived3 : TDerived2 { }
TBase inst = ...
if (inst is TDerived1)
{
// Handles case TDerived1
}
else if (inst is TDerived2)
{
// Handles cases TDerived2 and TDerived3
}
else if (inst is TDerived3)
{
// NOT EXECUTED <--- !
}
为“是” TDerived3
,因此在使用TDerived2
匹配时,两种情况均由较早的条件处理。这强调了“严格”或“精确”类型的 平等 与更强的类型 compatibility 之间的不同运行时语义。 >。因为OP的问题中的类型是is
原语(不能从其派生),所以区别无关紧要。但是,如果我们将接受的答案的“精确类型匹配”与上面显示的示例类进行匹配,我们将将获得不同的结果:
ValueType
实际上, C#7 甚至不会编译与先前显示的Type type = ...
switch (type)
{
case Type _ when type == typeof(TDerived1):
// Handles case TDerived1
break;
case Type _ when type == typeof(TDerived2):
// Handles case TDerived2
break;
case Type _ when type == typeof(TDerived3):
// Handles case TDerived3 <--- !
break;
}
序列相对应的switch
语句。 ( nb 似乎编译器应该将其检测为警告,而不是 error ,因为无害的结果只是无法访问的一个分支代码-编译器在其他地方认为警告的条件-并且还考虑到编译器甚至根本没有检测到if / else
版本中看似相同的情况)。就是这样:
在任何情况下,哪种替代行为是合适的,或者甚至很重要,将取决于您的应用程序,因此,我在这里的意思只是要提请注意这种区别。如果您确定需要更聪明的 type-compatibility 版本的switch方法,请按以下步骤操作:
if / else
最后,正如我在此页面上的另一个answer中所提到的,您可以进一步简化Type type = ...
switch (type)
{
case Type _ when type.IsAssignableFrom(typeof(TDerived1)):
// Handles case TDerived1
break;
case Type _ when type.IsAssignableFrom(typeof(TDerived2)):
// Handles cases TDerived2 and TDerived3
break;
case Type _ when type.IsAssignableFrom(typeof(TDerived3)):
// NOT EXECUTED <-- !
break;
}
语句的用法。由于我们仅使用switch
子句功能,并且由于我们大概仍可以在变量中使用原始的启动实例,因此无需在when
语句中提及该变量,也无需重复每个switch
中的类型(在这种情况下为Type
)。只需执行以下操作即可:
case
注意Type type = ...
switch (true)
{
case true when type.IsAssignableFrom(typeof(TDerived1)):
break;
case true when type.IsAssignableFrom(typeof(TDerived2)):
break;
case true when type.IsAssignableFrom(typeof(TDerived3)):
break;
}
和switch(true)
。每当您仅依靠case(true)
子句时(即,除了此处讨论的打开when
的情况之外),我都建议使用这种更简单的技术。
答案 3 :(得分:1)
@toddmo建议以下内容:
switch (true)
{
case bool _ when extruder.Temperature < 200:
HeatUpExtruder();
break;
// etc..
default:
StartPrintJob();
break;
}
...但是为什么不进一步追求简单。以下内容同样适用,不需要bool
类型限定,也不需要多余的_
虚拟变量:
switch (true)
{
case true when extruder.Temperature < 200:
HeatUpExtruder();
break;
// etc.
default:
StartPrintJob();
break;
}
答案 4 :(得分:0)
我找到了一种简单有效的方法。它需要C#V7才能运行。 switch("")
表示所有案例都将满足when
条款。它使用when
子句来查看type
。
List<Object> parameters = new List<object>(); // needed for new Action
parameters = new List<object>
{
new Action(()=>parameters.Count.ToString()),
(double) 3.14159,
(int) 42,
"M-String theory",
new System.Text.StringBuilder("This is a stringBuilder"),
null,
};
string parmStrings = string.Empty;
int index = -1;
foreach (object param in parameters)
{
index++;
Type type = param?.GetType() ?? typeof(ArgumentNullException);
switch ("")
{
case string anyName when type == typeof(Action):
parmStrings = $"{parmStrings} {(param as Action).ToString()} ";
break;
case string egStringBuilder when type == typeof(System.Text.StringBuilder):
parmStrings = $"{parmStrings} {(param as System.Text.StringBuilder)},";
break;
case string egInt when type == typeof(int):
parmStrings = $"{parmStrings} {param.ToString()},";
break;
case string egDouble when type == typeof(double):
parmStrings = $"{parmStrings} {param.ToString()},";
break;
case string egString when type == typeof(string):
parmStrings = $"{parmStrings} {param},";
break;
case string egNull when type == typeof(ArgumentNullException):
parmStrings = $"{parmStrings} parameter[{index}] is null";
break;
default: throw new System.InvalidOperationException();
};
}
这会使parmStrings包含...
System.Action 3.14159, 42, M-String theory, This is a stringBuilder, parameter[5] is null
答案 5 :(得分:0)
除了上述答案外,如果您不需要在case语句中使用该值,您可以使用_ 。此语法可用于删除未使用的变量警告消息。
switch (this.value) {
case int _:
//Do something else without value.
break;
case decimal decimalValue:
this.value = Math.Max(Math.Min(decimalValue, Maximum), Minimum);
break;
}
答案 6 :(得分:0)
我知道这并不适用于所有情况,但是也许我的例子会对某人有所帮助。在ASP.NET Core中,我实现了自定义模型活页夹提供程序,并且必须根据模型类型解析活页夹类型。
最初的想法(除了if / else的of块,但我一直认为它可能会更短)是开关:
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
switch (context.Metadata.ModelType)
{
case Type _ when context.Metadata.ModelType == typeof(Model1):
return new BinderTypeModelBinder(typeof(Binder1));
case Type _ when context.Metadata.ModelType == typeof(Model2):
return new BinderTypeModelBinder(typeof(Binder2));
case Type _ when context.Metadata.ModelType == typeof(Model3):
return new BinderTypeModelBinder(typeof(Binder3));
}
return null;
}
与字典相同:
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
var bindersByType = new Dictionary<Type, Type>
{
{typeof(Model1), typeof(Binder1)},
{typeof(Model2), typeof(Binder2)},
{typeof(Model3), typeof(Binder3)}
};
return bindersByType.TryGetValue(context.Metadata.ModelType, out Type binderType) ? new BinderTypeModelBinder(binderType) : null;
}
该主意的积分发给@xxbbcc,他在第一个问题的评论中发布了此信息
答案 7 :(得分:0)
好吧,这可能会破坏您的代码约定,但是我使用这种方法;
Type.IsAssignableFrom()
在条件语句中更合适的情况下可以使用public String yesOrNoSelected(){
String tag = "";
List<WebElement> radioOptions = findElementsByXpath("//div[@class='rule-scope-radio-list']");
//Iterate thru both radio options and execute the JavaScript.
for(int i = 1; i <= radioOptions.size(); i++) {
var val = radioOptions.get(i).findElement(By.cssSelector("input[value='true']"));
if(val!=null){
tag = radioOptions.get(i).getTagName();
System.out.println(tag);
}
}
return tag;
}}
。
答案 8 :(得分:-1)
这是一个替代方案,由于缺少类而无法编译:
bool? runOnUI = queuedAction.RunOnUI; // N=null, T=true F=False
bool isOnUI = Statics.CoreDispatcher.HasThreadAccess;
bool isFF = queuedAction.IsFireAndForget; // just makes it easier to read the switch statement
if (false == queuedAction.IsFireAndForget)
IsOtherTaskRunning = true;
/* In the case statements below the string name is something like noFF_TN
* The compiler ignores the string name. I have used them here to represent
* the logic in the case statement: ( FF = FireAnd Forget, T=true, F=false, N = null, * means 'whatever')
*
* isFF_** = FireAndForget QueuedAction
* noFF_** = Not FireAndForget so Wait for Task to Finish (as opposed to Complete)
* ****_T* = We are already on the UI thread
* ****_F* = We are not on the UI thread
* ****_*T = Run on the UI thread.
* ****_*F = Do not run on UI thread
* ****_*N = Don't care so run on current thread
*
* The last character is a "bool?" representing RunOnUI. It has
* three values each of which has a different meaning */
bool isTask;
switch ("ignore")
{
/* Run it as an Action (not Task) on current Thread */
case string noFF_TT when !isFF && isOnUI && runOnUI == true:
case string isFF_TN when isFF && isOnUI && !runOnUI == null:
case string isFF_FN when isFF && !isOnUI && runOnUI == null:
case string isFF_TT when isFF && isOnUI && runOnUI == true:
case string isFF_FF when isFF && !isOnUI && runOnUI == false:
isTask = false;
queuedAction.ActionQA(queuedAction); // run as an action, not as a Task
break;
/* Create a Task, Start it */
case string isFF_TF when isFF && isOnUI == true && runOnUI == false:
case string noFF_TN when !isFF && isOnUI == true && runOnUI == null: // <== not sure if I should run it on UI instead
case string noFF_TF when !isFF && isOnUI && runOnUI == false:
case string noFF_FN when !isFF && !isOnUI && runOnUI == null:
case string noFF_FF when !isFF && !isOnUI && runOnUI == false:
var cancellationTokenSource = new CancellationTokenSource();
queuedAction.Canceller?.SetCancellationTokenSource(cancellationTokenSource);
isTask = true;
new Task
(
(action) => queuedAction.ActionQA(queuedAction),
queuedAction,
cancellationTokenSource.Token
)
.Start();
break;
/* Run on UI and don't wait */
case string isFF_FT when isFF && !isOnUI && runOnUI == true:
isTask = true;
Statics.RunOnUI(() => queuedAction.ActionQA(queuedAction), asTaskAlways: true);
break;
/* Run on the UI as a Task and Wait */
case string noFF_FT when !isFF && !isOnUI && runOnUI == true:
isTask = true;
Statics.RunOnUI(() => queuedAction.ActionQA(queuedAction), asTaskAlways: true);
break;
default:
throw new LogicException("unknown case");
}