C#のLINQの関数であるCast()
の使い方についてです。
配列やリストなどの要素を指定した型に変換することが出来ます。
この記事には.NET Framework 4.6.1を使用しています。
この型に変換するよ
とあるクラスを継承したクラスがたくさんあり、そのデータの管理に一番ベースとなっているクラスのリストをデータとして持つ。
そんなときありますよね。
そのリストの要素を纏めてキャストしたいときはLINQのCast()
があります。
public static IEnumerable<TResult> Cast<TResult>( this IEnumerable source );
Enumerable.Cast(TResult) メソッド (IEnumerable) (System.Linq)
以下の例ではこのような構成のクラスに対してCast()
を使っています。
Program.cs
using System.Linq; using System.Collections; using System.Collections.Generic; public static class Program { private class BaseClass { public virtual string Type { get { return "-"; } } public string Name { get; set; } public override string ToString() { return string.Format( "Type:{0}, Name:{1}", Type, Name ); } } private class ClassA : BaseClass { public override string Type { get { return "A"; } } } private class ClassB : BaseClass { public override string Type { get { return "B"; } } } private class ClassC : ClassB { public override string Type { get { return "C"; } } } static void Main( string[] args ) { ClassB[] parametersA = new ClassB[] { new ClassB() { Name = "正一郎" }, new ClassB() { Name = "清次郎" }, new ClassB() { Name = "誠三郎" }, new ClassC() { Name = "征史郎" }, }; BaseClass[] parametersB = new BaseClass[] { new ClassB() { Name = "正一郎" }, new ClassB() { Name = "清次郎" }, new ClassB() { Name = "誠三郎" }, new ClassC() { Name = "征史郎" }, }; // アップキャスト IEnumerable<BaseClass> reaultUp = parametersA.Cast<BaseClass>(); // ダウンキャスト IEnumerable<ClassB> reaultDown = parametersB.Cast<ClassB>(); // 結果発表 System.Console.WriteLine( "parameters :{0}", parametersA.Text() ); System.Console.WriteLine( "アップキャスト:{0}", reaultUp.Text() ); System.Console.WriteLine( "ダウンキャスト:{0}", reaultDown.Text() ); // 入力待ち用 System.Console.ReadKey(); } /// <summary> /// 簡易的なシーケンスのテキスト取得処理 /// </summary> public static string Text( this IEnumerable i_source ) { string text = string.Empty; foreach( var value in i_source ) { text += string.Format( "[{0}], ", value ); } return text; } } // class Program
parameters :[Type:B, Name:正一郎], [Type:B, Name:清次郎], [Type:B, Name:誠三郎
], [Type:C, Name:征史郎],
アップキャスト:True
ダウンキャスト:True
変換出来ない場合はどうなるの
さて、ではCast()
をつかった際に、もしキャストが出来ないものがあった場合はどうなるのでしょうか。
Program.cs
using System.Linq; using System.Collections; using System.Collections.Generic; public static class Program { private class BaseClass { public virtual string Type { get { return "-"; } } public string Name { get; set; } public override string ToString() { return string.Format( "Type:{0}, Name:{1}", Type, Name ); } } private class ClassA : BaseClass { public override string Type { get { return "A"; } } } private class ClassB : BaseClass { public override string Type { get { return "B"; } } } private class ClassC : ClassB { public override string Type { get { return "C"; } } } static void Main( string[] args ) { BaseClass[] parameters = new BaseClass[] { new ClassB() { Name = "正一郎" }, new ClassB() { Name = "清次郎" }, new ClassB() { Name = "誠三郎" }, new ClassC() { Name = "征史郎" }, }; bool reault = false; try { // ClassC以外のクラスもあるから、成功しそうにないけど……。 reault = parameters.Cast<ClassC>().Any(); } catch( System.Exception i_exception ) { System.Console.WriteLine( "{0}", i_exception ); System.Console.ReadKey(); return; } // 結果発表 System.Console.WriteLine( "parameters :{0}", parameters.Text() ); System.Console.WriteLine( "cast :{0}", reault ); // 入力待ち用 System.Console.ReadKey(); } /// <summary> /// 簡易的なシーケンスのテキスト取得処理 /// </summary> public static string Text( this IEnumerable i_source ) { string text = string.Empty; foreach( var value in i_source ) { text += string.Format( "[{0}], ", value ); } return text; } } // class Program
System.InvalidCastException: 型 'ClassB' のオブジェクトを型 'ClassC' にキャストできません。
その場合は、例外が発生してしまいます。
OfTypeとCastはなにが違うの
LINQにはCast()
に似た関数としてOfType()
があります。
www.urablog.xyz
この関数の違いはなんなのかと。
てっきり、例外が発生するか否かの違いだと思っていました。
調べたところ、このような記事を見かけました。
なるほど、is
とas
の違いから要素がnull
だった場合に挙動が変わってくるわけですね。
試してみましょう。
Program.cs
using System.Linq; using System.Collections; using System.Collections.Generic; public static class Program { private class BaseClass { public virtual string Type { get { return "-"; } } public string Name { get; set; } public override string ToString() { return string.Format( "Type:{0}, Name:{1}", Type, Name ); } } private class ClassA : BaseClass { public override string Type { get { return "A"; } } } private class ClassB : BaseClass { public override string Type { get { return "B"; } } } private class ClassC : ClassB { public override string Type { get { return "C"; } } } static void Main( string[] args ) { BaseClass[] parameters = new BaseClass[] { new ClassB() { Name = "正一郎" }, null, new ClassB() { Name = "誠三郎" }, new ClassC() { Name = "征史郎" }, }; int countCast = parameters.Cast<ClassB>().Count(); int countOfType = parameters.OfType<ClassB>().Count(); // 結果発表 System.Console.WriteLine( "parameters :{0}", parameters.Text() ); System.Console.WriteLine( "countCast :{0}", countCast ); System.Console.WriteLine( "countOfType :{0}", countOfType ); // 入力待ち用 System.Console.ReadKey(); } /// <summary> /// 簡易的なシーケンスのテキスト取得処理 /// </summary> public static string Text( this IEnumerable i_source ) { string text = string.Empty; foreach( var value in i_source ) { text += string.Format( "[{0}], ", value ); } return text; } } // class Program
parameters :[Type:B, Name:正一郎], [], [Type:B, Name:誠三郎], [Type:C, Name:征
史郎],
countCast :4
countOfType :3
なるほど、確かに結果が違いますね。
null
の場合、Cast()
なら成功扱いになりますが、OfType()
の場合は失敗扱いになります。
この辺りに気を付けて、Cast()
を七兆回ほど使ってみてください。
LINQのリンク
LINQ一覧
www.urablog.xyzCast
指定した型のシーケンスに変換したい!
www.urablog.xyz