在 C# 中将泛型显式转换为另一种类型
Explicit Casting Generic to Another Type in C#
我有以下 C++ 代码,在代表一个点的模板化 class 中。我想将它翻译成 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" 是未定义的。这是有道理的……但是 how/where 我要定义 U 吗?我的方法不对吗?
我的问题与 here 的问题之间的区别在于,我只是通过强制转换更改泛型 class 的底层类型...而不是尝试更改一个泛型 class 转换为具有相同底层类型的不同泛型 class。
据我所知,只有在 T
和 U
.
之间存在某种继承关系时,才允许在 C# 中使用这种泛型转换
最接近的等效项是为转换定义通用方法:
public Point<U> To<U>()
{
dynamic p = this;
return new Point<U>((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));
}
您不能定义泛型转换运算符,因此您需要它是一个显式函数。此外,简单的转换 (U)t
将不起作用,因此您需要 Convert.ChangeType
(如果您的类型是数字,它将起作用)。
用法:
var p1 = new Point<int> { X = 1, Y = 2, Z = 3 };
var p2 = p1.As<double>();
类似于,但没有dynamic
是使用双投:
public Point<U> To<U>()
{
return new Point<U>((U)(object)X, (U)(object)Y, (U)(object)Z);
}
我们的两个答案在编译时都没有发现任何问题。
您不能使用额外的泛型类型参数声明运算符,但您可以声明特定泛型类型的运算符或来自特定泛型类型的运算符,例如 Point<int>
。 C# 也不允许您通过从 T
.
转换或转换为 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>
,然后在尝试转换时会在运行时抛出。
您不能定义额外的泛型类型约束,但您可以使用运算符和方法来做类似的事情。
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;
我有以下 C++ 代码,在代表一个点的模板化 class 中。我想将它翻译成 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" 是未定义的。这是有道理的……但是 how/where 我要定义 U 吗?我的方法不对吗?
我的问题与 here 的问题之间的区别在于,我只是通过强制转换更改泛型 class 的底层类型...而不是尝试更改一个泛型 class 转换为具有相同底层类型的不同泛型 class。
据我所知,只有在 T
和 U
.
最接近的等效项是为转换定义通用方法:
public Point<U> To<U>()
{
dynamic p = this;
return new Point<U>((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));
}
您不能定义泛型转换运算符,因此您需要它是一个显式函数。此外,简单的转换 (U)t
将不起作用,因此您需要 Convert.ChangeType
(如果您的类型是数字,它将起作用)。
用法:
var p1 = new Point<int> { X = 1, Y = 2, Z = 3 };
var p2 = p1.As<double>();
类似于dynamic
是使用双投:
public Point<U> To<U>()
{
return new Point<U>((U)(object)X, (U)(object)Y, (U)(object)Z);
}
我们的两个答案在编译时都没有发现任何问题。
您不能使用额外的泛型类型参数声明运算符,但您可以声明特定泛型类型的运算符或来自特定泛型类型的运算符,例如 Point<int>
。 C# 也不允许您通过从 T
.
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>
,然后在尝试转换时会在运行时抛出。
您不能定义额外的泛型类型约束,但您可以使用运算符和方法来做类似的事情。
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;