C#显式运算符和对象

Sor*_*ati 8 c# typeconverter explicit-conversion

请先查看代码.

这是我的自定义类:

public class float2D
{
    public float X { get; private set; }
    public float Y { get; private set; }

    public float2D(float x, float y)
    {
        this.X = x;
        this.Y = y;
    }

    public static explicit operator Point(float2D x)
    {
        return new Point((int)x.X, (int)x.Y);
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

这是我写的测试代码:

    private void TEST()
    {
        float2D V = new float2D(1, 1);
        Point P = Point.Empty;
        P = (Point)V; // Works
        P = (Point)(V as object); // Specified cast is not valid.
    }
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,当值类型未知时,它无法转换该值.我相信这是因为它在Object类中搜索操作数而不是实际类型.我怎么解决这个问题?

我有一个代码,其中一切都是对象,它必须处理这些对话.

如果您有任何想法,请告诉我.


避免动态

好吧,让我们以你能看到我想做什么的方式改变样本,以及我的情况.

这是我的课程:

    class TEST
    {
        dynamic OBJECT;
        public void STORE<T>(ref T V)
        {
            this.OBJECT = V;
        }

        public T CONVERT<T>()
        {
            return (T)this.OBJECT;
        }
    }
Run Code Online (Sandbox Code Playgroud)

这是测试代码:

        float2D V = new float2D(1, 1);
        TEST t = new TEST();
        t.STORE(ref V);
        Point P = t.CONVERT<Point>();
Run Code Online (Sandbox Code Playgroud)

有没有办法让我从课堂上放下动态并让它保持工作?我真的想避免使用.Net4/4.5

Mar*_*ell 15

是的,这两个截然不同的东西.第一行:

P = (Point)V; // Works
Run Code Online (Sandbox Code Playgroud)

使用显式转换运算符,该运算符为此组合重载.然而,第二个:

P = (Point)(V as object); // Specified cast is not valid.
Run Code Online (Sandbox Code Playgroud)

这将引用转换Vobject(我们通常知道它) - 然后单独转换objectPoint.

既然Point是a struct,那么它会尝试"取消装箱"相同的引用,就好像它是盒装的一样Point.但它不是盒装Point,所以错误是正确的.转换运算符不是多态的,只有当包含相应类型的盒装副本时才允许取消装箱(警告:您可以将枚举作为整数取消,将整数作为枚举 - 只要基础类型匹配).

但是,即使Point是a class,它仍会因类似错误而失败:如果类型不兼容,则引用保留转换也不起作用.


Chr*_*air 3

正如其他答案所述,您不能执行此操作,因为您正在尝试将运行时转换应用于编译时转换操作。

dynamic如果您因为不想使用 .NET 4.0 的 DLR 而想避免,则可以使用反射自行查找转换运算符。但是,无法评论您执行此操作的特定应用程序的性能:

public static TOutgoing Convert<TOutgoing>(object obj)
{
    Type incomingType = obj.GetType();

    MethodInfo conversionOperator = null;
    foreach(var method in incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public))
    {
        if (
            method.Name == "op_Explicit" && //explicit converter
            method.ReturnType == typeof(TOutgoing) && //returns your outgoing ("Point") type
            method.GetParameters().Length == 1 && //only has 1 input parameter
            method.GetParameters()[0].ParameterType == incomingType //parameter type matches your incoming ("float2D") type
            )
        {
            conversionOperator = method;
            break;
        }
    }

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

    throw new Exception("No conversion operator found");
}
Run Code Online (Sandbox Code Playgroud)

或者对于带有 LINQ 的 .NET 3.5:

public static TOutgoing Convert<TOutgoing>(object obj)
{
    Type incomingType = obj.GetType();

    var conversionOperator = incomingType.GetMethods(BindingFlags.Static | BindingFlags.Public)
        .Where(m => m.Name == "op_Explicit")
        .Where(m => m.ReturnType == typeof(TOutgoing))
        .Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == incomingType)
        .FirstOrDefault();

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

    throw new Exception("No conversion operator found");
}
Run Code Online (Sandbox Code Playgroud)

用法:

float2D V = new float2D(1, 1);
Point P = Point.Empty;
P = Convert<Point>(V); //passes
P = Convert<Point>((V as object)); //passes
Run Code Online (Sandbox Code Playgroud)

如果您希望通过隐式运算符进行转换,也可以添加“op_Implicit”。


如果您想避免反射,另一个选择是预先注册转换函数、一些转换和类型查找以确定要使用哪个转换运算符。

只是需要注意的是,这里的解决方案有几个问题(线程安全、假设转换函数存在、注册冲突/重复转换函数会引发错误),因此使用时需要您自担风险,或者将其用作修改指南,以满足您的需求。

基本要点是定义一个简单的转换器来包装转换函数本身:

public interface IConverter
{
    object Convert(object incomingObject);
}

public class Converter<TIncoming, TOutgoing> : IConverter
{
    private Func<TIncoming, TOutgoing> ConversionFunction;

    public Converter(Func<TIncoming, TOutgoing> conversionFunction)
    {
        this.ConversionFunction = conversionFunction;
    }

    public object Convert(object incomingObject)
    {
        TIncoming typedIncomingObject = (TIncoming)incomingObject;
        return ConversionFunction(typedIncomingObject);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以使用一个实用程序来注册这些转换:

public static class ConversionUtility
{
    private static Dictionary<Type, Dictionary<Type, IConverter>> Converters = new Dictionary<Type, Dictionary<Type, IConverter>>();

    public static void RegisterConversion<TIncoming, TOutgoing>(Func<TIncoming, TOutgoing> conversionFunction)
    {
        if (!Converters.ContainsKey(typeof(TIncoming)))
        {
            Converters[typeof(TIncoming)] = new Dictionary<Type, IConverter>();
        }

        Converters[typeof(TIncoming)].Add(typeof(TOutgoing), new Converter<TIncoming, TOutgoing>(conversionFunction));
    }

    public static TOutgoing Convert<TOutgoing>(object obj)
    {
        Type incomingType = obj.GetType();

        IConverter converter = Converters[incomingType][typeof(TOutgoing)];

        return (TOutgoing)converter.Convert(obj);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用时,首先您必须注册您希望在应用程序中使用的转换函数(最好在应用程序启动时执行注册以避免线程问题):

ConversionUtility.RegisterConversion((float2D obj) => (Point)obj);
Run Code Online (Sandbox Code Playgroud)

然后你的转换使用:

float2D V = new float2D(1, 1);
Point P = Point.Empty;
P = ConversionUtility.Convert<Point>(V); //passes
P = ConversionUtility.Convert<Point>((V as object)); //passes
Run Code Online (Sandbox Code Playgroud)

对于您的特定应用程序使用情况,不确定其中一种的性能优于另一种。第一个示例更加灵活,因为它在运行时执行检查,并且您不必预先注册您期望使用的转换。第二种可能更稳定一些,因为您只注册您期望使用的转换,并且没有反射,只有转换和字典查找。