C中的另一个类型的显式转换泛型# < P>我在C++中使用了下面的代码,它在一个表示一个点的模板类中。我想把它翻译成C#: 模板 类点 { 公众: tx; T y; tz; 模板显式点(常数点和p) :x((T)p.x),y((T)p.y),z((T)p.z) { } }

C中的另一个类型的显式转换泛型# < P>我在C++中使用了下面的代码,它在一个表示一个点的模板类中。我想把它翻译成C#: 模板 类点 { 公众: tx; T y; tz; 模板显式点(常数点和p) :x((T)p.x),y((T)p.y),z((T)p.z) { } },c#,generics,casting,explicit,C#,Generics,Casting,Explicit,此代码允许将给定类型的点显式转换为另一类型的点。例如,您可以这样使用它(诚然,我不是100%确定这里的语法,但我明白了这个概念): p点; q点=(点)p; 如何在C#中启用与此等效的功能?到目前为止,我已经: public class Point<T> { public T X { get; set; } public T Y { get; set; } public T Z { get; set; } // Constructors exist

此代码允许将给定类型的点显式转换为另一类型的点。例如,您可以这样使用它(诚然,我不是100%确定这里的语法,但我明白了这个概念):

p点;
q点=(点)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)
    {

    }
}
公共类点
{
公共tx{get;set;}
公共T{get;set;}
公共tz{get;set;}
//存在接受X、Y和Z作为参数的构造函数
公共静态显式运算符点(点p)
{
}
}
但是,这会给出一个错误,即“U”未定义。这是有道理的。。。但是我如何/在哪里定义你?我的方法不对吗


我的问题和这个问题的区别在于,我只是通过一个cast来更改泛型类的底层类型。。。不尝试将一个泛型类更改为具有相同底层类型的不同泛型类。

据我所知,只有在
T
U
之间存在某种继承关系的情况下,才允许在C中使用这种泛型强制转换

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

public Point<U> To<U>()
{
    dynamic p = this;

    return new Point<U>((U)p.X, (U)p.Y, (U)p.Z);
}
公共点到()
{
动态p=此;
返回新点((U)p.X,(U)p.Y,(U)p.Z);
}

您不能直接将
T
转换为
U
,因为编译器无法知道它是否安全。我使用
dynamic
关键字来绕过这个限制。

我认为你能得到的最好结果是:

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));
}
()。

类似于,但没有
dynamic
是使用双重强制转换:

public Point<U> To<U>()
{
    return new Point<U>((U)(object)X, (U)(object)Y, (U)(object)Z);
}
公共点到()
{
返回新点((U)(对象)X,(U)(对象)Y,(U)(对象)Z);
}

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

您不能用其他泛型类型参数声明运算符,但可以向特定泛型类型(如
Point
)或从特定泛型类型声明运算符。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));
    }    
}
公共类点,其中T:IConvertible
{
// ...
公共静态显式运算符点(点)
{
//IFormatProvider参数对纯数字转换没有影响
返回新点(Point.X.ToInt32(null)、Point.Y.ToInt32(null)、Point.Y.ToInt32(null));
}    
}

但是,这不会阻止用户声明无意义的、不受支持的类型,例如
Point
,当尝试转换时,该类型将在运行时抛出。

您不能定义其他泛型类型约束,但可以使用运算符和方法执行类似操作

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));
    }
}
公共类点
{
公共tx{get;set;}
公共T{get;set;}
公共tz{get;set;}
公共静态显式运算符点(点v)
{
返回v.As();
}
公共静态显式运算符点(点v)
{
返回v.As();
}
公共静态显式运算符点(点v)
{
返回v.As();
}
公共点As()
{
返回新点()
{
X=转换为(X),
Y=转换为(Y),
Z=转换为(Z)
};
}
专用静态TU转换器(T)
{
return(TU)Convert.ChangeType(t,typeof(TU));
}
}
用法:

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

        Point<int> i = (Point<int>) d;

        Point<float> f = (Point<float>) i;

        d = (Point<double>) f;
点d=新点()
{
X=10d,Y=10d,Z=10d
};
点i=(点)d;
点f=(点)i;
d=(点)f;

转换运算符不能是泛型的。是否需要
U
作为泛型参数?您可以用实际类型替换
U
。当然,您只能使用有限的转换类型。请注意,
Convert.ChangeType
仅适用于实现
IConvertible
的类型。如果OP坚持使用primitive,这是非常好的types@KevinGosse:是的,我在回答中说“如果您的类型是数字,那么它将起作用”。谢谢你的提示!这将不起作用,您需要在强制转换之前取消绑定该值<代码>整数i=(整数)(对象)1.0f抛出一个
InvalidCastException
Hm。哦,是的,你是对的。我在一些通用设计中使用了双类型转换,但它总是用于引用类型。这看起来更通用,因为它不需要数字类型。我的问题发生在(只会使用数字类型)…但如果不是这样的话,我们很高兴知道这一点。我确实想知道这与Convert.ChangeType选项之间的性能区别是什么。@MichaelKintscher这是个好问题。我运行了一个快速基准测试,
Convert.ChangeType
得到127ns,而
Dynamic
得到53ns。请注意,对dynamic的第一次调用要昂贵得多(因为表达式是在第一次调用期间计算的,然后缓存的),很好知道!我还添加了
where U:IConvertible
,作为
T
U
的约束条件,以确保我的类消费者的安全。
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));
    }
}
        Point<double> d = new Point<double>()
        {
            X = 10d, Y = 10d, Z = 10d
        };

        Point<int> i = (Point<int>) d;

        Point<float> f = (Point<float>) i;

        d = (Point<double>) f;