web-dev-qa-db-ja.com

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

C#では、object型の変数をT型の変数にキャストできます。ここでTはType型変数で定義されていますか。

233
theringostarrs

ここでできることは確かに単純な(これがT型キャストであると仮定します)キャストであり、都合が良い場合にはa(これをTに変換できると仮定します)の変換です。

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

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

編集:

コメント内の何人かの人々は、この答えは質問に答えないと言っています。しかし(T) Convert.ChangeType(input, typeof(T))という行が解決策を提供します。 Convert.ChangeTypeメソッドは、任意のObjectを2番目の引数として指定されたTypeに変換しようとします。

例えば:

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

// Variable value2 is now an int with a value of 1000
object value2a = Convert.ChangeType(value1, intType);
int value2b = Convert.ChangeType(value1, intType);

// Variable value3 is now an int with a value of 1000
dynamic value3 = Convert.ChangeType(value1, intType);

実際の型を処理せずにa somethinga something elseにキャストしたい場合は、コード臭の可能性が非常に高いと思われるので、私は総称で答えを書きました。適切なインターフェースを使えば、99.9%の回数必要ではないはずです。理にかなっているかもしれないという反射に関しては、おそらく少数のEdgeのケースがありますが、私はそれらのケースを避けることをお勧めします。

163
Zyphrax

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

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

「動的」を使用すると、コンパイラは静的型チェックを回避するため、注意しないと実行時エラーが発生する可能性があります。

92
maulik13

これは、動的に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キーワードとは異なり、キャストはコード内では行われず、オブジェクトの基になるデータのみが変換されます。コンパイル時には、私たちのオブジェクトがどんな型であるのかを正確に把握することが依然として残されているため、この解決策は実用的ではありません。私はこれを任意の型で定義された変換演算子を呼び出すためのハックとして書きましたが、おそらく誰かがもっと良いユースケースを見つけることができるでしょう。

19
balage

簡単にするためにボクシングとアンボクシングを別にして、継承階層に沿ってキャストすることに関与する特定のランタイムアクションはありません。それはほとんどコンパイル時のことです。基本的に、キャストは、変数の値を別の型として扱うようにコンパイラーに指示します。

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

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

どうやってそれをすることができますか?キャスト後にオブジェクトを格納できる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?
6

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);

Int値で複数の列挙型のDescription属性値を取得する場合、これは私にとって不可欠でした。

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));
    }
2
krzyski

Zyphraxの答えを使用するときに「オブジェクトはIConvertibleを実装する必要があります」という例外を回避するための何かを見つけることができなかった後。

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

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);
0
Curt
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}
0
user2008563