直接投数组和使用System.Linq.Cast有什么区别?
What is the difference between directly casting an array or using System.Linq.Cast?
假设我有 2 个 类、A
和 B
,并且 B
可以转换为 A
。我声明了一个名为 b
的 B[]
类型的数组。那如果我想把b
转换成A[]
,(A[])b
和b.Cast<A>()
有什么区别?
.Cast<T>
来自 Linq。它将枚举将每个项目转换为 T
的集合并创建一个新的序列。另一个是显式转换,告诉编译器您希望访问原始类型。
这是两个不同的东西。
语言转换
(A[])b
将 b
转换为类型 A[]
,如果 b
不是 A[]
的类型,则不会编译或在运行时抛出异常。
以双精度和整数为例:
var array = new object[2];
array[0] = 10.2;
array[1] = 20.8;
var casted = (int[])array; // does not compile here,
// or throw an exception at runtime if types mismatch
这里我们只是将一种类型转换为另一种类型,不管它们是什么,集合与否。
Casting and type conversions (C# Programming Guide)
Linq Cast
Cast<TResult>
将 IEnumerable
的每个项目转换为 TResult
。
这只是一个已经编写好的 LINQ 循环,让我们的生活更轻松 boxed 值。
Enumerable.Cast(IEnumerable) Method
Casts the elements of an IEnumerable to the specified type.
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source)
{
foreach (object obj in source) yield return (TResult)obj;
}
因此,此方法可用于从 DataGridView
中的 Rows
或任何类似的“简化”集合(例如 Items
中的 [=] 中取消装箱值26=] 或 ComboBox
.
这意味着项目的类型必须是 TResult
或祖先的类型。
例子
var array = new object[2];
array[0] = 10.2;
array[1] = 20.8;
var converted = array.Cast<int>(); // compiles but will not work
// and throw an InvalidCastException
备注
因为yield,Cast
方法被延迟了,所以我们只有在执行时才得到结果,例如使用foreach
或ToList
.
Deferred Execution of LINQ Query
Deferred Vs Immediate Query Execution in LINQ
Deferred execution and lazy evaluation
替代解决样本上的问题
因此要转换数组,我们可以使用直接转换,例如使用 foreach
或 Select
:
var converted = array.Select(v => (int)v).ToArray(); // get int[]
Console.WriteLine(string.Join(Environment.NewLine, converted));
> 10
> 20
使用扩展方法
static public class EnumerableHelper
{
static public IEnumerable<TResult> Cast<TSource, TResult>(this IEnumerable<TSource> source)
where TSource : IConvertible
{
foreach ( TSource obj in source )
yield return (TResult)Convert.ChangeType(obj, typeof(TResult));
}
}
var converted = array.Cast<double, int>();
> 10
> 21
还有 CultureInfo.InvariantCulture
来避免数字问题,还有一个格式化程序来避免四舍五入。
你的两个例子虽然不同,但都是无效的。
您不能将一种对象类型的数组转换为另一种对象类型,即使它们之间存在转换运算符(显式或隐式)。编译器正确地阻止了这样的转换。该规则的例外情况是是否存在继承关系;由于数组协变,您可以向下转换为基本类型(对于引用类型)。以下作品:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = (A[])bs; // valid
同样的原则也适用于 LINQ 中的 Cast<T>
方法——除非类型匹配,否则将在运行时 根据枚举 抛出异常。下面的答案是不正确的。例如,您不能 Cast
double
到 int
的数组。当然,如果你不枚举结果(如例子中)则不会发生异常。但是,在实际枚举 (foreach
、ToList
、ToArray
) 时,将抛出 InvalidCastException
。
var array = new double[2];
array[0] = 10;
array[1] = 20;
var temp = array.Cast<int>(); // OK, not enumerated
var converted = temp.ToList(); // bam! InvalidCastException
注意 temp
变量——因为在下面的答案中,由于 LINQ 的延迟执行,它不会抛出。一旦你枚举它,它就会失败。参见 SharpLab。
Cast
方法旨在弥合与前泛型集合之间的差距,在前泛型集合中,值在内部存储为 object
的数组,而集合本身仅实现 IEnumerable
。 Cast
允许转换为 IEnumerable<T>
,但是除了从 object
到原始类型之外,不允许 casting/converting。
对于结构,这是显而易见的——装箱的 double
只能拆箱为 double
;它无法拆箱为 int
。以简单的非数组情况为例:
double d = 1.5;
object o = d;
int iOk = (int)(double)o; // ok
int iBad = (int)o; // fails
那么,Cast<int>
将失败是有道理的,因为该方法仅将单个转换插入到 int
,而 而不是 将中间转换插入到 double
否则将需要。
对于类,再次Cast
将只插入直接转换。该方法是通用的,并且 not/cannot 考虑到任何用户定义的运算符。因此,当您说“有两个 类 可以相互转换”时,这仍然无关紧要。换句话说,以下将失败:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
var temp = bs.Cast<A>(); // OK, not yet enumerated
A[] result = temp.ToArray(); // throws InvalidCastException
同样(如上所述),此规则的例外情况是两者之间存在继承关系 类。你可以从一个人沮丧到另一个人:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = bs.Cast<A>().ToArray(); // valid
一种替代方法是使用 LINQ 的 Select
来投影您的原始集合,应用您想要的转换运算符:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
A[] result = bs.Select(b => (A)b).ToArray(); // valid!
参见 SharpLab。这也适用于 double
/int
:
var array = new double[] { 10.2, 20.4 };
int[] result = array.Select(d => (int)d).ToArray();
假设我有 2 个 类、A
和 B
,并且 B
可以转换为 A
。我声明了一个名为 b
的 B[]
类型的数组。那如果我想把b
转换成A[]
,(A[])b
和b.Cast<A>()
有什么区别?
.Cast<T>
来自 Linq。它将枚举将每个项目转换为 T
的集合并创建一个新的序列。另一个是显式转换,告诉编译器您希望访问原始类型。
这是两个不同的东西。
语言转换
(A[])b
将 b
转换为类型 A[]
,如果 b
不是 A[]
的类型,则不会编译或在运行时抛出异常。
以双精度和整数为例:
var array = new object[2];
array[0] = 10.2;
array[1] = 20.8;
var casted = (int[])array; // does not compile here,
// or throw an exception at runtime if types mismatch
这里我们只是将一种类型转换为另一种类型,不管它们是什么,集合与否。
Casting and type conversions (C# Programming Guide)
Linq Cast
Cast<TResult>
将 IEnumerable
的每个项目转换为 TResult
。
这只是一个已经编写好的 LINQ 循环,让我们的生活更轻松 boxed 值。
Enumerable.Cast(IEnumerable) Method
Casts the elements of an IEnumerable to the specified type.
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source)
{
foreach (object obj in source) yield return (TResult)obj;
}
因此,此方法可用于从 DataGridView
中的 Rows
或任何类似的“简化”集合(例如 Items
中的 [=] 中取消装箱值26=] 或 ComboBox
.
这意味着项目的类型必须是 TResult
或祖先的类型。
例子
var array = new object[2];
array[0] = 10.2;
array[1] = 20.8;
var converted = array.Cast<int>(); // compiles but will not work
// and throw an InvalidCastException
备注
因为yield,Cast
方法被延迟了,所以我们只有在执行时才得到结果,例如使用foreach
或ToList
.
Deferred Execution of LINQ Query
Deferred Vs Immediate Query Execution in LINQ
Deferred execution and lazy evaluation
替代解决样本上的问题
因此要转换数组,我们可以使用直接转换,例如使用 foreach
或 Select
:
var converted = array.Select(v => (int)v).ToArray(); // get int[]
Console.WriteLine(string.Join(Environment.NewLine, converted));
> 10
> 20
使用扩展方法
static public class EnumerableHelper
{
static public IEnumerable<TResult> Cast<TSource, TResult>(this IEnumerable<TSource> source)
where TSource : IConvertible
{
foreach ( TSource obj in source )
yield return (TResult)Convert.ChangeType(obj, typeof(TResult));
}
}
var converted = array.Cast<double, int>();
> 10
> 21
还有 CultureInfo.InvariantCulture
来避免数字问题,还有一个格式化程序来避免四舍五入。
你的两个例子虽然不同,但都是无效的。
您不能将一种对象类型的数组转换为另一种对象类型,即使它们之间存在转换运算符(显式或隐式)。编译器正确地阻止了这样的转换。该规则的例外情况是是否存在继承关系;由于数组协变,您可以向下转换为基本类型(对于引用类型)。以下作品:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = (A[])bs; // valid
同样的原则也适用于 LINQ 中的 Cast<T>
方法——除非类型匹配,否则将在运行时 根据枚举 抛出异常。下面的答案是不正确的。例如,您不能 Cast
double
到 int
的数组。当然,如果你不枚举结果(如例子中)则不会发生异常。但是,在实际枚举 (foreach
、ToList
、ToArray
) 时,将抛出 InvalidCastException
。
var array = new double[2];
array[0] = 10;
array[1] = 20;
var temp = array.Cast<int>(); // OK, not enumerated
var converted = temp.ToList(); // bam! InvalidCastException
注意 temp
变量——因为在下面的答案中,由于 LINQ 的延迟执行,它不会抛出。一旦你枚举它,它就会失败。参见 SharpLab。
Cast
方法旨在弥合与前泛型集合之间的差距,在前泛型集合中,值在内部存储为 object
的数组,而集合本身仅实现 IEnumerable
。 Cast
允许转换为 IEnumerable<T>
,但是除了从 object
到原始类型之外,不允许 casting/converting。
对于结构,这是显而易见的——装箱的 double
只能拆箱为 double
;它无法拆箱为 int
。以简单的非数组情况为例:
double d = 1.5;
object o = d;
int iOk = (int)(double)o; // ok
int iBad = (int)o; // fails
那么,Cast<int>
将失败是有道理的,因为该方法仅将单个转换插入到 int
,而 而不是 将中间转换插入到 double
否则将需要。
对于类,再次Cast
将只插入直接转换。该方法是通用的,并且 not/cannot 考虑到任何用户定义的运算符。因此,当您说“有两个 类 可以相互转换”时,这仍然无关紧要。换句话说,以下将失败:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
var temp = bs.Cast<A>(); // OK, not yet enumerated
A[] result = temp.ToArray(); // throws InvalidCastException
同样(如上所述),此规则的例外情况是两者之间存在继承关系 类。你可以从一个人沮丧到另一个人:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = bs.Cast<A>().ToArray(); // valid
一种替代方法是使用 LINQ 的 Select
来投影您的原始集合,应用您想要的转换运算符:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
A[] result = bs.Select(b => (A)b).ToArray(); // valid!
参见 SharpLab。这也适用于 double
/int
:
var array = new double[] { 10.2, 20.4 };
int[] result = array.Select(d => (int)d).ToArray();