c#7.0:打开System.Type

时间:2017-03-28 21:44:00

标签: c#

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

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

9 个答案:

答案 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版本中看似相同的情况)。就是这样:

enter image description here

在任何情况下,哪种替代行为是合适的,或者甚至很重要,将取决于您的应用程序,因此,我在这里的意思只是要提请注意这种区别。如果您确定需要更聪明的 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");
}