Type変数を使用して変数をキャストする


280

C#では、オブジェクトの変数をT型の変数にキャストできますが、TはType変数で定義されていますか?


12
厳密にはトピックではありませんが、「キャスト」が何であるかについては曖昧に思えますが、キャスト演算子の目的とセマンティクスが何であるかを正確に理解することは良い考えかもしれません。ここでは良いスタートだ:blogs.msdn.com/ericlippert/archive/2009/03/19/...
エリックリペット

2
何か思いついたと思った。Type変数がある場合は、リフレクションを使用してその型のインスタンスを作成できます。そして、ジェネリックメソッドを使用して、その型のパラメーターから推論することにより、目的の型を返すことができます。残念ながら、型のインスタンスを作成するリフレクションメソッドの戻り値の型はobjectであるため、ジェネリックCastByExampleメソッドでも使用さobjectれます。だから、これを行う方法は本当にありません。たとえあったとしても、新しくキャストされたオブジェクトをどうしますか?型がわからないため、メソッドなどを使用できませんでした。
カイルデラニー

@KyleDelaneyありがとう、私は完全に同意します!私の答えで説明しようとしたように、実際に使用しているTypeを定義することなく、何か別のものにキャストすることはそれほど役に立ちません。型の要点は、コンパイラー時の型チェックです。オブジェクトを呼び出すだけの場合は、objectまたはを使用できますdynamic。外部モジュールを動的にロードする場合は、クラスで共通のインターフェースを共有し、それにオブジェクトをキャストできます。サードパーティのコードを制御しない場合は、小さなラッパーを作成し、その上にインターフェイスを実装します。
Zyphrax

回答:


203

キャストとコンバートの例を次に示します。

using System;

public T CastObject<T>(object input) {   
    return (T) input;   
}

public T ConvertObject<T>(object input) {
    return (T) Convert.ChangeType(input, typeof(T));
}

編集:

コメントの中の何人かの人々は、この答えは質問には答えないと言います。しかし、ライン(T) Convert.ChangeType(input, typeof(T))は解決策を提供します。このConvert.ChangeTypeメソッドは、任意のオブジェクトを2番目の引数として提供されたタイプに変換しようとします。

例えば:

Type intType = typeof(Int32);
object value1 = 1000.1;

// Variable value2 is now an int with a value of 1000, the compiler 
// knows the exact type, it is safe to use and you will have autocomplete
int value2 = Convert.ChangeType(value1, intType);

// Variable value3 is now an int with a value of 1000, the compiler
// doesn't know the exact type so it will allow you to call any
// property or method on it, but will crash if it doesn't exist
dynamic value3 = Convert.ChangeType(value1, intType);

実際の型を処理せずにキャストa somethingしたい場合は、コードのにおいがする可能性が非常に高いため、ジェネリックで回答を記述しましたa something else。99.9%の時間で必要ではないはずの適切なインターフェース。リフレクションに関しては、それが理にかなっている可能性があるいくつかのエッジケースがあるかもしれませんが、それらのケースを避けることをお勧めします。

編集2:

いくつかの追加のヒント:

  • コードをできるだけタイプセーフに保つようにしてください。コンパイラーがタイプを認識していない場合、コードが正しいかどうかをチェックできず、オートコンプリートなどが機能しません。簡単に言うと、コンパイル時に型を予測できない場合、コンパイラーはどのようにできるでしょうか。
  • 使用しているクラスが共通のインターフェース実装している場合は、そのインターフェースに値をキャストできます。それ以外の場合は、独自のインターフェースの作成を検討し、クラスにそのインターフェースを実装させます。
  • 動的にインポートする外部ライブラリを使用している場合は、共通のインターフェースも確認してください。それ以外の場合は、インターフェイスを実装する小さなラッパークラスの作成を検討してください。
  • オブジェクトを呼び出したいがタイプは気にしない場合は、objectまたはdynamic変数に値を格納します。
  • ジェネリックスは、含まれる正確な型を知る必要なく、さまざまな型に適用される再利用可能なコードを作成するための優れた方法です。
  • 行き詰まっている場合は、別のアプローチまたはコードのリファクタリングを検討してください。あなたのコードは本当に動的でなければなりませんか?あるタイプを説明する必要がありますか?

145
私はこれがOPをどのように助けるか知りません。彼女は型変数を持っていますが、そうではありませんT
nawfal 2013

12
@nawfal、基本的にラインConvert.ChangeType(input, typeof(T));は解決策を与えます。typeof(T)既存の型変数と簡単に置き換えることができます。(可能であれば)より良い解決策は、動的な型をまとめて防ぐことです。
Zyphrax 2013

59
@Zyphrax、Tそれはまだ利用できないキャストが必要です。
nawfal 2013

4
結果のオブジェクトが実際にタイプであることは知っていますがT、それでもobject参照としてのみ取得します。うーん、OPにはType変数のみがあり、他の情報はないという前提で、質問が興味深いと思いました。メソッドのシグネチャがConvert(object source, Type destination):)であるかのように、それでもポイントを取得します
nawfal

10
これはこの質問の解決策ですか?同じ問題があり、汎用の<T>を持っていません。タイプ変数しかありません。
Nuri Tasdemir 2016年

114

他の回答では「動的」タイプについて言及していません。したがって、もう1つ回答を追加するには、「動的」タイプを使用して、静的タイプで変換されたオブジェクトをキャストする必要なく、結果のオブジェクトを格納できます。

dynamic changedObj = Convert.ChangeType(obj, typeVar);
changedObj.Method();

「動的」を使用すると、コンパイラーは静的型チェックをバイパスするため、注意しないと実行時エラーが発生する可能性があることに注意してください。


19
これが正解です。動的キーワードなしでは、typeof(changedObj)は「オブジェクト」です。dynamicキーワードを使用すると、問題なく機能し、typeof(changedObject)はtypeVarと同じ型を正しく反映します。さらに、型がわからない場合に実行できない(T)キャストは必要ありません。
rushinge 2015

5
このソリューションを使用しているときに、「オブジェクトはIConvertibleを実装する必要があります」という例外があります。何か助けは?
Nuri Tasdemir 2016年

@NuriTasdemir言うのは難しいですが、あなたがしている変換はIConvertibleなしでは不可能だと思います。変換に関係するタイプは何ですか?
maulik13

これは機能しますが、ダイナミクスを使用するとパフォーマンスが低下します。他のランタイムで作業している場合を除き、これらを使用しないことをお勧めします(これは、ダイナミクスが設計された目的です)。
ボロ

19

ここに、System.Type動的にではなく、ジェネリック型変数ではなくオブジェクトをキャストするための私のメソッドがあります:

System.Linq.Expressionsタイプのを使用して実行時にラムダ式を作成し、Func<object, object>その入力のボックス化を解除し、必要なタイプ変換を実行してから、結果をボックス化します。キャストされるすべてのタイプだけでなく、キャストされるタイプ(ボックス化解除のステップのため)にも新しいものが必要です。これらの式の作成は、内部で行われるリフレクション、コンパイル、および動的メソッドの構築のため、非常に時間がかかります。幸いなことに、一度作成すると、高いオーバーヘッドなしに式を繰り返し呼び出すことができるので、それぞれをキャッシュします。

private static Func<object, object> MakeCastDelegate(Type from, Type to)
{
    var p = Expression.Parameter(typeof(object)); //do not inline
    return Expression.Lambda<Func<object, object>>(
        Expression.Convert(Expression.ConvertChecked(Expression.Convert(p, from), to), typeof(object)),
        p).Compile();
}

private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>> CastCache
= new Dictionary<Tuple<Type, Type>, Func<object, object>>();

public static Func<object, object> GetCastDelegate(Type from, Type to)
{
    lock (CastCache)
    {
        var key = new Tuple<Type, Type>(from, to);
        Func<object, object> cast_delegate;
        if (!CastCache.TryGetValue(key, out cast_delegate))
        {
            cast_delegate = MakeCastDelegate(from, to);
            CastCache.Add(key, cast_delegate);
        }
        return cast_delegate;
    }
}

public static object Cast(Type t, object o)
{
    return GetCastDelegate(o.GetType(), t).Invoke(o);
}

これは魔法ではないことに注意してください。キャストはコードでは発生しません。dynamicキーワードの場合とは異なり、オブジェクトの基になるデータのみが変換されます。コンパイル時には、オブジェクトのタイプを正確に把握する必要があるため、このソリューションは実用的ではありません。私はこれを任意の型で定義された変換演算子を呼び出すためのハックとして書きましたが、多分誰かがもっと良いユースケースを見つけることができます。


2
必須using System.Linq.Expressions;
アーロンD

4
私にとって、これはジフラックスの答えと同じ問題に悩まされています。返されたオブジェクトはまだ「オブジェクト」タイプなので、メソッドを呼び出すことができません。「『T』は可変ですが、それはタイプのように使用されている-かどうか私は(T)のキャストで同じエラーを取得し、彼の方法(「」以下)、またはあなたの方法(以下「B」)を使用しています。Type t = typeof(MyGeneric<>).MakeGenericType(obj.OutputType); var a = (t)Convert.ChangeType(obj, t); var b = (t)Caster.Cast(t, obj);
muusbolla

@muusbolla Zyphraxの元の答えは、ジェネリックと型変数を使用し、ではありませんType。Typeオブジェクトしかない場合、通常のキャスト構文を使用してキャストすることはできません。実行時ではなくコンパイル時にオブジェクトを何らかのタイプTとして使用できるようにしたい場合は、タイプ変数または実際のタイプ名だけを使用してオブジェクトをキャストする必要があります。前者はZaphraxの答えを使用して実行できます。
アシュリー

8

単純化のためにボックス化とボックス化解除を別にしておくと、継承階層に沿ったキャストに関係する特定の実行時アクションはありません。これは主にコンパイル時の問題です。基本的に、キャストは、変数の値を別の型として扱うようにコンパイラーに指示します。

キャスト後に何ができますか?タイプがわからないので、その上でメソッドを呼び出すことができません。あなたができる特別なことはありません。具体的には、コンパイル時に可能な型がわかっていて、それを手動でキャストし、ifステートメントを使用して各ケースを個別に処理する場合にのみ役立ちます。

if (type == typeof(int)) {
    int x = (int)obj;
    DoSomethingWithInt(x);
} else if (type == typeof(string)) {
    string s = (string)obj;
    DoSomethingWithString(s);
} // ...

1
私の質問に関連して、それをより明確に説明していただけませんか?
theringostarrs 2009年

私が説明しようとしているのは、その後に何ができるでしょうか?C#コンパイラでは、オブジェクトで有用な処理を行うために静的型付けが必要であるため、多くのことを行うことはできません
Mehrdad Afshari

あなたが正しい。タイプ「オブジェクト」としてメソッドに送信される2つの変数の予想されるタイプを知っています。変数に格納されている予想される型にキャストし、それらをコレクションに追加したい。タイプで分岐し、通常のキャストを試みてエラーをキャッチする方がはるかに簡単です。
theringostarrs 2009年

4
あなたの答えは良いですが、一口に言っておくと、キャストは変数に影響を与えません。キャストする法的なことはありません変数を別の型の変数に。変数の型はC#では不変です。あなただけのキャストすることができます別の型に変数に格納します。
Eric Lippert、

C#4.0の動的型付けの導入により、この答えは変わりますか?
ダニエルT.

6

どうやってそれをすることができますか?キャスト後にオブジェクトを格納できるT型の変数またはフィールドが必要ですが、実行時にのみTを知っている場合、このような変数またはフィールドをどのように作成できますか?だから、いいえ、それは不可能です。

Type type = GetSomeType();
Object @object = GetSomeObject();

??? xyz = @object.CastTo(type); // How would you declare the variable?

xyz.??? // What methods, properties, or fields are valid here?

3
タイプTの戻り値を持つメソッドを定義するジェネリッククラスを使用している場合は、それを行う必要があります。たとえば、文字列をTのインスタンスに解析し、それを返します。
Oliver Friedrich、

7
幸い、これは正しい答えではありません。maulik13の回答を参照してください。
rushinge 2015

3
天国のどこでCastTo方法を見つけますObjectか?
ProfK

3

Enumタイプへのキャストに関しては:

private static Enum GetEnum(Type type, int value)
    {
        if (type.IsEnum)
            if (Enum.IsDefined(type, value))
            {
                return (Enum)Enum.ToObject(type, value);
            }

        return null;
    }

そして、あなたはそれをそのように呼びます:

var enumValue = GetEnum(typeof(YourEnum), foo);

これは、いくつかの列挙型のDescription属性値をint値で取得する場合に不可欠でした。

public enum YourEnum
{
    [Description("Desc1")]
    Val1,
    [Description("Desc2")]
    Val2,
    Val3,
}

public static string GetDescriptionFromEnum(Enum value, bool inherit)
    {
        Type type = value.GetType();

        System.Reflection.MemberInfo[] memInfo = type.GetMember(value.ToString());

        if (memInfo.Length > 0)
        {
            object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), inherit);
            if (attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }

        return value.ToString();
    }

その後:

string description = GetDescriptionFromEnum(GetEnum(typeof(YourEnum), foo));
string description2 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum2), foo2));
string description3 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum3), foo3));

あるいは(より良いアプローチ)、そのようなキャストは次のようになります。

 private static T GetEnum<T>(int v) where T : struct, IConvertible
    {
        if (typeof(T).IsEnum)
            if (Enum.IsDefined(typeof(T), v))
            {
                return (T)Enum.ToObject(typeof(T), v);
            }

        throw new ArgumentException(string.Format("{0} is not a valid value of {1}", v, typeof(T).Name));
    }

1

Zyphraxの回答を使用すると、「オブジェクトはIConvertibleを実装する必要があります」という例外を見つけられなかった後(インターフェースの実装を除く)。

Newtonsoft.Json nugetパッケージを使用しています...

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);

1

害、問題はあなたがTを持っていないことです。

Type変数しかありません。

MSへのヒント。

TryCast<typeof(MyClass)>

私たちのすべての問題を解決するかどうか。


0

コメントを残すために最大50の評判が必要な理由は決してわかりませんが、@ Curtの回答は私が探していたものであり、他の誰かであるといいのですが。

私の例では、jsonパッチドキュメントの値を更新するために使用していたActionFilterAttributeがあります。パッチドキュメントのTモデルが何であるかはわかりませんでした。それを単純なJsonPatchDocumentにシリアル化および逆シリアル化し、それを変更する必要がありました。それから、型があったので、シリアル化および逆シリアル化して再び型に戻しました。

Type originalType = //someType that gets passed in to my constructor.

var objectAsString = JsonConvert.SerializeObject(myObjectWithAGenericType);
var plainPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument>(objectAsString);

var plainPatchDocumentAsString= JsonConvert.SerializeObject(plainPatchDocument);
var modifiedObjectWithGenericType = JsonConvert.DeserializeObject(plainPatchDocumentAsString, originalType );

-1
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}

2
この回答が他の回答とどのように異なり、このソリューションが適切であるかを指摘していただけませんか?
KlausGütter19年

-2

さらにきれい:

    public static bool TryCast<T>(ref T t, object o)
    {
        if (!(o is T))
        {
            return false;
        }

        t = (T)o;
        return true;
    }

-2

宛先タイプを知らずに実行時にオブジェクトをキャストする必要がある場合は、リフレクションを使用して動的コンバーターを作成できます。

これは簡略化されたバージョンです(生成されたメソッドをキャッシュしない):

    public static class Tool
    {
            public static object CastTo<T>(object value) where T : class
            {
                return value as T;
            }

            private static readonly MethodInfo CastToInfo = typeof (Tool).GetMethod("CastTo");

            public static object DynamicCast(object source, Type targetType)
            {
                return CastToInfo.MakeGenericMethod(new[] { targetType }).Invoke(null, new[] { source });
            }
    }

それを呼び出すことができます:

    var r = Tool.DynamicCast(myinstance, typeof (MyClass));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.