在C#中将泛型强制转换为另一种类型

时间:2019-02-20 20:49:38

标签: c# generics casting explicit

在代表点的模板化类中,我有以下C ++代码。我想将其翻译成C#:

template <class T>
class Point
{
    public:
        T x;
        T y;
        T z;

    template<typename U> explicit Point(const Point<U> &p)
       : x((T)p.x), y((T)p.y), z((T)p.z)
    {
    }
}

此代码允许将给定类型的点显式转换为另一种类型的点。例如,您可以使用类似的名称(诚然,我不确定此处的语法是否100%正确,但我明白了):

Point<float> p;
Point<int> q = (Point<int>)p;

如何在C#中启用与此等效的功能?到目前为止,我有:

public class Point<T>
{
    public T X { get; set; }
    public T Y { get; set; }
    public T Z { get; set; }

    // Constructors exist that accept X, Y, and Z as parameters

    public static explicit operator Point<U>(Point<T> p)
    {

    }
}

这给出了一个错误,但是说“ U”未定义。这很有道理...但是我如何/在哪里定义U?我的方法不正确吗?

我的问题和here的问题之间的区别在于,我只是通过强制转换来更改泛型类的底层类型...而不是尝试将一个泛型类更改为具有相同泛型的另一泛型类底衬类型。

5 个答案:

答案 0 :(得分:2)

据我所知,只有在console.log($(table)[0].config.widgetOptions); editable_autoAccept: true editable_autoResort: false editable_columns: (7) [7, 8, 9, 10, 11, 12, 13] T之间存在某种继承关系时,C#才允许这种通用类型转换。

最接近的等效项是为转换定义通用方法:

U

您不能直接将public Point<U> To<U>() { dynamic p = this; return new Point<U>((U)p.X, (U)p.Y, (U)p.Z); } 转换为T,因为编译器无法知道它是否安全。我使用U关键字来绕过该限制。

答案 1 :(得分:2)

我认为您能得到的最好的是:

public class Point<T>
{
    public T X { get; set; }
    public T Y { get; set; }
    public T Z { get; set; }

    public Point<U> As<U>()
    {
        return new Point<U>()
        {
            X = Convert<U>(X),
            Y = Convert<U>(Y),
            Z = Convert<U>(Z)
        };
    }

    static U Convert<U>(T t) => (U)System.Convert.ChangeType(t, typeof(U));
}

您无法定义通用转换运算符,因此需要将其用作显式函数。此外,简单的强制转换(U)t将不起作用,因此您需要Convert.ChangeType(如果您的类型是数字,则可以使用)。

用法:

var p1 = new Point<int> { X = 1, Y = 2, Z = 3 };
var p2 = p1.As<double>();

works as expected)。

答案 2 :(得分:2)

类似于Kevin's answer,但没有dynamic的是使用双重强制转换:

public Point<U> To<U>()
{
    return new Point<U>((U)(object)X, (U)(object)Y, (U)(object)Z);
}

我们两个答案在编译时都没有发现任何问题。

答案 3 :(得分:1)

您不能使用其他泛型类型参数声明运算符,但是可以向诸如Point<int>之类的特定泛型类型声明运算符。 C#也不允许您通过强制转换为T来执行任意转换。

保持最小类型安全性的最简单的选择就是将T参数限制为IConvertible

public class Point<T> where T : IConvertible
{
    // ...

    public static explicit operator Point<int>(Point<T> point)
    {
        // The IFormatProvider parameter has no effect on purely numeric conversions
        return new Point<int>(point.X.ToInt32(null), point.Y.ToInt32(null), point.Y.ToInt32(null));
    }    
}

但是,这不会阻止用户声明毫无意义,不受支持的类型,例如Point<DateTime>,这些类型会在尝试转换时在运行时抛出。

答案 4 :(得分:1)

您不能定义其他通用类型约束,但是可以使用运算符和方法来做类似的事情。

{{ myDate: date:'short':'+0100'}}

用法:

public class Point<T>
{
    public T X { get; set; }
    public T Y { get; set; }
    public T Z { get; set; }

    public static explicit operator Point<T>(Point<int> v)
    {
        return v.As<T>();
    }

    public static explicit operator Point<T>(Point<double> v)
    {
        return v.As<T>();
    }

    public static explicit operator Point<T>(Point<float> v)
    {
        return v.As<T>();
    }

    public Point<TU> As<TU>()
    {
        return new Point<TU>()
        {
            X = ConvertTo<TU>(X),
            Y = ConvertTo<TU>(Y),
            Z = ConvertTo<TU>(Z)
        };
    }

    private static TU ConvertTo<TU>(T t)
    {
        return (TU) Convert.ChangeType(t, typeof(TU));
    }
}