英文:
C# implicit cast for generic struct
问题
I have a generic class that acts as some wrapper for my other classes that are wrappers for some defined BCL classes
I want to create an implicit cast from the original BCL class to my intermediate class.
To be clear, here is an example:
public interface IWrapper<T>{
}
public struct IntWrapper : IWrapper<int>{
public IntWrapper(int i)
{
}
}
public struct MainWrapper<T, TW> where TW: IWrapper<T>
{
public TW field;
public static implicit operator MainWrapper<T, TW>(int val)
=> new MainWrapper<T, TW>{field = new IntWrapper(val)}; // <-- Here the code fails
}
The code fails with an error that IntWrapper cannot be cast to TW.
What I tried:
- Explicit cast
(TW)new IntWrapper(val)
- Explicit set of return value types
new MainWrapper<int, IntWrapper>
- Explicit set of return types
MainWrapper<int, IntWrapper>(int val)
None of them work and fail for different reasons.
Is there a way to do this cast?
Update
If I move this implicit cast to an intermediate type and split it into 2 casts (from int to IntWrapper and then from IntWrapper to MainWrapper<int>), it works.
But in this case, in the usage method, I need to help the compiler find the chain of casts by adding an explicit cast to this intermediate class:
public interface IWrapper<T>
{
}
public struct IntWrapper : IWrapper<int>
{
public IntWrapper(int i)
{
}
public static implicit operator IntWrapper(int val)
=> new IntWrapper(val);
public static implicit operator MainWrapper<int, IntWrapper>(IntWrapper val)
=> new MainWrapper<int, IntWrapper> { field = val };
}
public struct MainWrapper<T, TW> where TW : IWrapper<T>
{
public TW field;
}
public class UseExample
{
public void Do(MainWrapper<int, IntWrapper> data){}
public void Test()
{
Do((IntWrapper)1);
}
}
Is there a way to help the compiler find this chain without an explicit cast on the usage side?
英文:
I have a generic class that acts as some wrapper for my other classes that are wrappers for some defined BCL classes
I want to create implicit cast from original BCL class to my using the intermediate class.
To be clear here is an example:
public interface IWrapper<T>{
}
public struct IntWrapper : IWrapper<int>{
public IntWrapper(int i)
{
}
}
public struct MainWrapper<T, TW> where TW: IWrapper<T>
{
public TW field;
public static implicit operator MainWrapper<T, TW>(int val)
=> new MainWrapper<T, TW>{field = new IntWrapper(val)}; // <-- Here the code fails
}
The code fails with error that IntWrapper can not be casted to TW.
What I tried:
- Explicit cast
(TW)new IntWrapper(val)
- Explicit set of return values types
new MainWrapper<int, IntWrapper>
- Explicit set of return types
MainWrapper<int, IntWrapper>(int val)
None of them works and fails for different reasons.
Is there a way to do this cast?
Update
If I move this implicit casts to intermediate type and split into 2 casts (from int to IntWrapper and then from IntWrapper to MainWrapper<int>) it works.
But in this case In the usage method I need to help compiler to find the chain of casts through adding explicit cast to this intermediate class:
public interface IWrapper<T>
{
}
public struct IntWrapper : IWrapper<int>
{
public IntWrapper(int i)
{
}
public static implicit operator IntWrapper(int val)
=> new IntWrapper(val);
public static implicit operator MainWrapper<int, IntWrapper>(IntWrapper val)
=> new MainWrapper<int, IntWrapper> { field = val };
}
public struct MainWrapper<T, TW> where TW : IWrapper<T>
{
public TW field;
}
public class UseExample
{
public void Do(MainWrapper<int, IntWrapper> data){}
public void Test()
{
Do((IntWrapper)1);
}
}
Is there a way to help compiler to find this chain without explicit cast on a usage side?
答案1
得分: 3
代码部分未翻译。以下是翻译好的部分:
"You get the error since your TW
type is not the same as IWrapper<int>
. After all you could write your assignment like MainWrapper<int, MyOtherIntWrapper> i = 5;
, and MyOtherIntWrapper
is not the same as IntWrapper
, even if they happen to implement the same interface."
"你遇到这个错误是因为你的 TW
类型与 IWrapper<int>
不一样。毕竟,你可以像这样编写你的赋值语句:MainWrapper<int, MyOtherIntWrapper> i = 5;
,而 MyOtherIntWrapper
与 IntWrapper
不同,即使它们恰好实现了相同的接口。"
"If you change your code a bit you can get rid of that specific error:"
"如果你稍微修改你的代码,就可以摆脱那个特定的错误:"
"But you still get an User defined conversion must convert from or to the enclosing type-error. Because MainWrapper<int>
is not the same thing as MainWrapper<T>
. The only way I know to avoid this would be to declare the conversion on the int-type, but that is obviously not possible."
"但你仍然会遇到 User defined conversion must convert from or to the enclosing type 错误。因为 MainWrapper<int>
不同于 MainWrapper<T>
。我知道的唯一避免这个错误的方法是在 int 类型上声明转换,但显然这是不可能的。"
"You could force the cast:"
"你可以强制进行强制转换:"
"public static implicit operator MainWrapper<T, TW>(int val) => new MainWrapper<T, TW> { field = (TW)((object)(new IntWrapper(val))) };"
"public static implicit operator MainWrapper<T, TW>(int val) => new MainWrapper<T, TW> { field = (TW)((object)(new IntWrapper(val))) };"
"But that is really not something I would recommend. While MainWrapper<int, IWrapper<int>> i1 = 5;
would work, MainWrapper<int[], IWrapper<int[]>> i2 = 5;
will fail at runtime, and that is much worse."
"但这并不是我建议的方法。虽然 MainWrapper<int, IWrapper<int>> i1 = 5;
可以工作,但 MainWrapper<int[], IWrapper<int[]>> i2 = 5;
在运行时会失败,这更糟糕。"
"So in the end I would suggest that you reconsider whatever it is you are trying to do. Generics are great as long as you can accept any type:"
"因此,最后我建议你重新考虑你试图做的事情。只要你可以接受 任何 类型,泛型就非常棒:"
"public struct MainWrapper<T> {
public T field;
public static implicit operator MainWrapper<T>(T val) => new MainWrapper<T> { field = val };
}"
"public struct MainWrapper<T> {
public T field;
public static implicit operator MainWrapper<T>(T val) => new MainWrapper<T> { field = val };
}"
"But as sone as you start introducing type specific stuff it tend to get messy."
"但一旦你开始引入特定类型的东西,事情就会变得混乱。"
英文:
You get the error since your TW
type is not the same as IWrapper<int>
. After all you could write your assignment like MainWrapper<int, MyOtherIntWrapper> i = 5;
, and MyOtherIntWrapper
is not the same as IntWrapper
, even if they happen to implement the same interface.
If you change your code a bit you can get rid of that specific error:
public struct MainWrapper<T>
{
public IWrapper<T> field;
public static implicit operator MainWrapper<int>(int val)
=> new MainWrapper<int> { field = new IntWrapper(val) };
}
But you still get an User defined conversion must convert from or to the enclosing type-error. Because MainWrapper<int>
is not the same thing as MainWrapper<T>
. The only way I know to avoid this would be to declare the conversion on the int-type, but that is obviously not possible.
You could force the cast:
public static implicit operator MainWrapper<T, TW>(int val)
=> new MainWrapper<T, TW> { field = (TW)((object)(new IntWrapper(val))) };
But that is really not something I would recommend. While MainWrapper<int, IWrapper<int>> i1 = 5;
would work, MainWrapper<int[], IWrapper<int[]>> i2 = 5;
will fail at runtime, and that is much worse.
So in the end I would suggest that you reconsider whatever it is you are trying to do. Generics are great as long as you can accept any type:
public struct MainWrapper<T> {
public T field;
public static implicit operator MainWrapper<T>(T val) => new MainWrapper<T> { field = val };
}
But as sone as you start introducing type specific stuff it tend to get messy.
答案2
得分: 0
使用通用约束,您只能强制类型具有空构造函数。对于其他构造函数,您可以假设 Activator.CreateInstance
将起作用,但可能会引发运行时错误。
在 C#11 中,虽然仍然无法强制执行或调用特定构造函数,但您可以定义 static abstract
接口成员。因此,您的接口可以强制实现一个静态方法,用作工厂方法。
注意,通过"奇怪的递归"约束,接口可以定义此工厂的实际返回类型,而不是返回接口的装箱实例。
public interface IWrapper<T, TW>
where TW : IWrapper<T, TW>
{
static abstract TW Factory(T value);
}
public struct IntWrapper : IWrapper<IntWrapper, int>
{
public static IntWrapper Factory(int value)
=> new IntWrapper(value);
}
然后,在通用代码中,您可以根据通用参数的类型调用此工厂方法:
public static implicit operator MainWrapper<T, TW>(T val)
where TW : IWrapper<T, TW>
=> new MainWrapper<T, TW>{ field = TW.Factory(val) };
英文:
With a generic constraint, you can only enforce that a type has an empty constructor. For other constructors you can assume that Activator.CreateInstance
will work, but will risk throwing runtime errors.
In C#11, while you still can't enforce or call a particular constructor, you can define static abstract
interface members. So your interface can enforce that the implementation implements a static method to act as a factory method.
Note, with a "curiously recursive" constraint, the interface can define the actual return type of this factory, instead of returning a boxed instance of the interface.
public interface IWrapper<T, TW>
where TW : IWrapper<T, TW>
{
static abstract TW Factory(T value);
}
public struct IntWrapper : IWrapper<IntWrapper, int>
{
public static IntWrapper Factory(int value)
=> new IntWrapper(value);
}
Then in generic code, you can call this factory method based on the type of a generic parameter;
public static implicit operator MainWrapper<T, TW>(T val)
where TW : IWrapper<T, TW>
=> new MainWrapper<T, TW>{ field = TW.Factory(val) };
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论