问题描述
C#模式匹配是否支持多种类型?例如,我们可以做类似的事情吗?
if(value is float v || value is double v)
这当然是行不通的;不能两次使用变量名。同样,我们不能使用两个不同的名称,例如v1
和v2
,因为那样我们将如何引用正确匹配的模式变量,从而有效地终止了模式匹配的目的。
编辑
只是为了使事情收敛,这就是我最终得到的结果(没有模式匹配):
public object Convert(object[] values,Type targettype,object parameter,CultureInfo culture)
{
if (values != null && values.Length == 3 && (values[0] is float || values[0] is double || values[0] is decimal) && (values[1] is float || values[1] is double || values[1] is decimal) && (values[2] is float || values[2] is double || values[2] is decimal))
return (float)values[0] * (float)values[1] * (float)values[2];
else
throw new ArgumentException("All parameters must be of numeric type.");
}
涉及到一些垂头丧气,但出于我的目的,它们不会造成大量信息丢失。
在我理想的世界中,我希望这样写:
public object Convert(object[] values,CultureInfo culture)
{
if (values != null && values.Length == 3 &&
values[0] is [float,double,decimal] v1 &&
values[1] is [float,decimal] v2 &&
values[2] is [float,decimal] v3 &&
)
return v1 * v2 * v3;
else
throw new ArgumentException("All parameters must be of numeric type.");
}
显然没有什么用,只是为了分享我的想法。 :)
解决方法
不,不是。
您最好的选择是编写两个模式匹配的if
声明(或if
/ else if
组合,具体取决于您的首选逻辑流程),并使用两个结果类型为{ {1}}和float
分别作为同一功能的输入,因此double
块内的代码不会重复。
编辑:
在OP澄清他们希望匹配的变量具有相同的名称之前发布了答案。保留原来的答案:
您可以在switch
中执行类似的操作-可能会更有用,因为根据类型的不同,您可能需要不同的代码。
object test = 25.5f;
switch (test)
{
case float f:
Console.WriteLine("I'm a float: " + f);
break;
case double d:
Console.WriteLine("I'm a double: " + d);
break;
}
,
如何整理LINQ:
select *
我会毫不犹豫地建议强制转换为浮点数,因为如果发送双精度数可能不合适,等等。(并且如注释中所指出的那样,如果将小数作为对象装箱,则不能将其拆箱,并且使其同时漂浮)
您可能最终会对所有内容进行public object Convert(object[] values,Type targetType,object parameter,CultureInfo culture)
{
if ((values?.Length ?? 0) == 3 && values.All(v => v is double || v is decimal || v is float)
...
else
throw new ArgumentException("All parameters must be of numeric type.");
}
或类似操作:
Convert.ToDecimal
:)
,不,它不会;最初的问题从根本上来说是有缺陷的,因为C#不支持union types,所以这样的重复变量名 不能用来指代“两种”值。这意味着没有变量v
可以强类型为float|double
1 。将变量键入为对象是原始问题。
此外,C#不允许有意义地使用 关于方法/算法本身,以相同的方式编写这两种方法时,需要相同数量的保护和强制转换来实现解决方案;仅不同变量的数量发生变化。但是,这在使用强制转换的原始实现中被一个错误掩盖了。 错误显示的“旧样式”强制转换代码具有 将原始代码切换为使用 1 泛型在这里无济于事,因为运算符不是多态的,并且没有有效的方法来约束T来解决此问题(例如,没有ISummable)。 2 此答案不对等效形式做出判断。唯一的区别是命名变量的数目和正在转换的表达式的求值数目。如果为每种类型使用直接强制转换正确地编写了原始代码,以免导致运行时失败,则强制转换的次数(以及组合的数目)与使用防护的次数相同。 3 这里是说明问题的一种方法,同时说明了“完全隐藏”和“浮起来”效果。如Jard的回答所示,我可能会直接将守卫 拉入序列循环。 然后使用固定数量的变量: 或在一系列对象之间的用法: 这还将处理固定的按序列设置:if (x is A a || x is B b)
。这是因为未分配 a
或未分配 b
,从而有条件地使用了毒药案中的任何一个变量,并且编译失败。相反,使用if (x is A a && x is B b)
是可行的保护措施,因为两者 a
和b
都可以得到分配。
(float)x
,其中x键入为object
。对于双精度和十进制值,这将在运行时失败。正确强制转换的唯一方法是使用正确的强制转换,每个强制转换都有正确完善的防护每次强制转换,无论是捕获到变量还是其他变量: 2 object x = (double)1;
// InvalidCastException - can’t cast System.Single to System.Double
float f = (float)x;
// ok! cast to double,convert to float
float f2 = (float)(double)x;
Convert.ToSingle
可以解决此问题,同时继续隐藏函数内部的有效转换类型,并且采用其他算法 3 而不是保护每个值类型。通过更改算法,首先将每个值转换为浮点数,它还减少了类型之间求和的组合总数。一个等效的本地方法可以用3个警卫来编写。
float? ToFloat(object x) {
if (x is float f) return f;
if (x is double d) return (float)d;
if (x is decimal m) return (float)m;
return null;
}
if (v != null && v.Length >= 3) {
var v0 = ToFloat(v[0]);
var v1 = ToFloat(v[1]);
var v2 = ToFloat(v[2]);
if (v0.HasValue && v1.HasValue && v2.HasValue)
return v0.Value + v1.Value + v2.Value;
}
throw ..
float r = 0;
foreach (var o in seq) {
var v = ToFloat(o);
if (!v.HasValue)
throw ..
r += v.Value;
}
return r;
foreach (var o in new[] { v[2],v[4],v[6] }) ..