c#7.0:打开System.Type

没有现有问题可以回答这个问题。

在c#7中,我可以直接在System.Type上切换吗?

当我尝试:

  switch (Type) { case typeof(int): break; } 

它告诉我typeof(int)需要是一个常量表达式。

是否有一些合成糖允许我避免使用case nameof(int):并直接比较相等的类型? case语句中的nameof(T)并不完全正确,因为名称空间。 因此,虽然名称冲突可能不适用于int ,但它适用于其他比较。

换句话说,我试图比这更直接:

  switch (Type.Name) { case nameof(Int32): case nameof(Decimal): this.value = Math.Max(Math.Min(0, Maximum), Minimum); // enforce minimum break; } 

(已经链接的)新模式匹配function允许这样做。

通常,你打开一个值:

 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; } 

请注意,这不是该function的用途,它变得不如传统的ifelse ifelse ifelse链,并且传统链是它无论如何编译的内容。 我不建议像这样使用模式匹配。

从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做过类似的事情,所以我习惯了这种forms(但在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) (对于不是常量表达式的情况),如果我们不想使用字符串常量,在类型名称的情况下,我们会遇到类似于此的东西,不在框架中。

我找到了一种简单有效的方法。 它需要C#V7才能运行。 switch("")表示所有情况都将满足when子句。 它使用when子句来查看type

 List parameters = new List(); // needed for new Action parameters = new List { 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

这是一个替代方案,由于缺少类而无法编译:

 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"); }