簡體   English   中英

如何在C#泛型類中調用類型轉換運算符?

[英]How to call type conversion operator in C# generic class?

似乎我們不能在C#泛型類中輕松調用類型轉換運算符。 這是代碼。 為什么?

T006最終存檔了我們的目標。

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Linq;

namespace ConsoleApplication1
{
    class vec<T, T2> : List<T> where T : class
    {
        public vec(IEnumerable<T2> other)
        {
            //Converter<T2, T> cvt = (v) => (T)v; // T004 failed, try defined function dynamicly, cannot compile, too.

            // T006 pass, client happy, we not happy, but anyway passed, performance may not happy.
            var conversionOperator = typeof(T).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                    .Where(m => m.Name == "op_Explicit" || m.Name == "op_Implicit")
                                    .Where(m => m.ReturnType == typeof(T))
                                    .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(T2))
                                    .FirstOrDefault();
            Func<T2, T> cvt = (obj) =>
            {
                if (conversionOperator != null)
                    return (T)conversionOperator.Invoke(null, new object[] { obj });
                else
                    return default(T);
            };

            foreach (T2 item in other)
            {
                //Add((T)item);         // T001 failed, this line cannot compile
                //Add(item as T);       // T002 failed, this line alwasy return null. //  http://msdn.microsoft.com/en-us/library/vstudio/cscsdfbt.aspx
                //Add((T)(object)item); // T003 failed, pass compile, but throw exception at runtime.
                Add(cvt(item));         // T006 pass.
            }
        }

        // T005 pass, but clients for this code will not happy.
        public vec(Converter<T2, T> cvt, IEnumerable<T2> other)
        {
            foreach (T2 item in other)
            {
                Add(cvt(item));
            }

        }
    }

    class XXX
    {
        public int foo = 22;
        static public explicit operator XXX(YYY other)
        {
            XXX me = new XXX();
            me.foo = (int)other.foo;
            return me;
        }
    }

    class YYY
    {
        public float foo = 11;
    }



    class Program
    {
        static void Main(string[] args)
        {
            YYY[] from = new YYY[2];
            for (int i = 0; i < from.Length; i++)
            {
                from[i] = new YYY();
            }

            XXX x = (XXX)from[0];

            vec<XXX, YYY> coll = new vec<XXX, YYY>(from);

            // Not happy, this requires user have strong C# skill;
            //vec<XXX, YYY> coll = new vec<XXX, YYY>((v) => (XXX)v, from);

            foreach (var item in coll)
            {
                Debug.Print("Value is {0}", item.foo);
            }

        }
    }

}

T001的編譯器錯誤是: Cannot convert type 'T2' to 'T'

如果您需要運行自定義的隱式/顯式轉換運算符,則對object強制轉換(或執行as cast)將跳過它們,因為僅在執行運行時強制轉換時才在編譯時知道該信息。

通過您所發布的通用通用方法,我知道的唯一方法是利用dynamic ,該方法將在運行時進行查找以查看是否定義了任何轉換運算符並調用它們:

return (T2)(dynamic)obj;

快速示例:

public class Class1
{

    public static implicit operator Class1(Class2 class2)
    {
        Console.WriteLine("implicit conversion from Class2 to Class1");
        return new Class1();
    }

    public static implicit operator Class2(Class1 class1)
    {
    Console.WriteLine("implicit conversion from Class1 to Class2");
        return new Class2();
    }
}

public class Class2
{

}

public static T2 Convert<T1, T2>(T1 obj)
{
    return (T2)(dynamic)obj;
}

var class1 = new Class1();
var class2 = Convert<Class1, Class2>(class1);
//outputs: implicit conversion from Class1 to Class2

但是請注意,此操作使用了反射並在運行時做了很多工作,因此請進行全面測試並確保性能仍然可以接受。

編輯:由於您無權訪問動態語言運行庫,因此可以使用反射來編寫自己的轉換運算符查找:

public static T2 Convert<T1, T2>(T1 obj)
{
    var conversionOperator = typeof(T1).GetMethods(BindingFlags.Static | BindingFlags.Public)
    .Where(m => m.Name == "op_Explicit" || m.Name == "op_Implicit")
    .Where(m => m.ReturnType == typeof(T2))
    .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(T1))
    .FirstOrDefault();

    if (conversionOperator != null)
        return (T2)conversionOperator.Invoke(null, new object[]{obj});

    throw new Exception("No conversion operator found");
}

您可能需要調整代碼(如果找不到操作符,則可能嘗試進行傳統的轉換),而且我不確定是否可以保證每次都能正常工作。 我不知道是否需要處理極端情況或平台怪癖。 更不用說反射會很慢了。 您可以引入一種快速緩存機制,在該機制中,您可以使用Dictionary進行O(1)查找,或者可以存儲針對每種類型組合找到的每個轉換運算符。

您可以先將其轉換為object然后轉換為T

Add((T)(object)item)

但是您應該謹慎對待運行時錯誤,並以不會引起問題的方式定義TT2

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM