C中的另一个类型的显式转换泛型# < P>我在C++中使用了下面的代码,它在一个表示一个点的模板类中。我想把它翻译成C#: 模板 类点 { 公众: tx; T y; tz; 模板显式点(常数点和p) :x((T)p.x),y((T)p.y),z((T)p.z) { } }
此代码允许将给定类型的点显式转换为另一类型的点。例如,您可以这样使用它(诚然,我不是100%确定这里的语法,但我明白了这个概念):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
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;