C# 泛型结构的隐式转换

huangapple go评论68阅读模式
英文:

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 C# 泛型结构的隐式转换

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&lt;T&gt;
    {

    }
    public struct IntWrapper : IWrapper&lt;int&gt;
    {
        public IntWrapper(int i)
        {
        }

        public static implicit operator IntWrapper(int val)
            =&gt; new IntWrapper(val);

        public static implicit operator MainWrapper&lt;int, IntWrapper&gt;(IntWrapper val)
            =&gt; new MainWrapper&lt;int, IntWrapper&gt; { field = val };

    }

    public struct MainWrapper&lt;T, TW&gt; where TW : IWrapper&lt;T&gt;
    {
        public TW field;
    }

    public class UseExample
    {
        public void Do(MainWrapper&lt;int, IntWrapper&gt; 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 C# 泛型结构的隐式转换

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&lt;T&gt;{

    }
    public struct IntWrapper : IWrapper&lt;int&gt;{
        public IntWrapper(int i)
        {
        }
    }

    public struct MainWrapper&lt;T, TW&gt; where TW: IWrapper&lt;T&gt;
    {
        public TW field;

        public static implicit operator MainWrapper&lt;T, TW&gt;(int val) 
            =&gt; new MainWrapper&lt;T, TW&gt;{field = new IntWrapper(val)}; // &lt;-- 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&lt;int, IntWrapper&gt;
  • Explicit set of return types MainWrapper&lt;int, IntWrapper&gt;(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&lt;T&gt;
    {

    }
    public struct IntWrapper : IWrapper&lt;int&gt;
    {
        public IntWrapper(int i)
        {
        }

        public static implicit operator IntWrapper(int val)
            =&gt; new IntWrapper(val);

        public static implicit operator MainWrapper&lt;int, IntWrapper&gt;(IntWrapper val)
            =&gt; new MainWrapper&lt;int, IntWrapper&gt; { field = val };

    }

    public struct MainWrapper&lt;T, TW&gt; where TW : IWrapper&lt;T&gt;
    {
        public TW field;
    }

    public class UseExample
    {
        public void Do(MainWrapper&lt;int, IntWrapper&gt; 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&lt;int&gt;. After all you could write your assignment like MainWrapper&lt;int, MyOtherIntWrapper&gt; i = 5;, and MyOtherIntWrapper is not the same as IntWrapper, even if they happen to implement the same interface."

"你遇到这个错误是因为你的 TW 类型与 IWrapper&lt;int&gt; 不一样。毕竟,你可以像这样编写你的赋值语句:MainWrapper&lt;int, MyOtherIntWrapper&gt; i = 5;,而 MyOtherIntWrapperIntWrapper 不同,即使它们恰好实现了相同的接口。"

"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&lt;int&gt; is not the same thing as MainWrapper&lt;T&gt;. 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&lt;int&gt; 不同于 MainWrapper&lt;T&gt;。我知道的唯一避免这个错误的方法是在 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&lt;int, IWrapper&lt;int&gt;&gt; i1 = 5; would work, MainWrapper&lt;int[], IWrapper&lt;int[]&gt;&gt; i2 = 5; will fail at runtime, and that is much worse."

"但这并不是我建议的方法。虽然 MainWrapper&lt;int, IWrapper&lt;int&gt;&gt; i1 = 5; 可以工作,但 MainWrapper&lt;int[], IWrapper&lt;int[]&gt;&gt; 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&lt;int&gt;. After all you could write your assignment like MainWrapper&lt;int, MyOtherIntWrapper&gt; 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&lt;T&gt; 
{
    public IWrapper&lt;T&gt; field;

    public static implicit operator MainWrapper&lt;int&gt;(int val)
        =&gt; new MainWrapper&lt;int&gt; { field = new IntWrapper(val) };
}

But you still get an User defined conversion must convert from or to the enclosing type-error. Because MainWrapper&lt;int&gt; is not the same thing as MainWrapper&lt;T&gt;. 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&lt;T, TW&gt;(int val)
            =&gt; new MainWrapper&lt;T, TW&gt; { field = (TW)((object)(new IntWrapper(val))) }; 

But that is really not something I would recommend. While MainWrapper&lt;int, IWrapper&lt;int&gt;&gt; i1 = 5; would work, MainWrapper&lt;int[], IWrapper&lt;int[]&gt;&gt; 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&lt;T&gt; {
    public T field;
    public static implicit operator MainWrapper&lt;T&gt;(T val) =&gt; new MainWrapper&lt;T&gt; { 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&lt;T, TW&gt;
    where TW : IWrapper&lt;T, TW&gt;
{
    static abstract TW Factory(T value);
}

public struct IntWrapper : IWrapper&lt;IntWrapper, int&gt;
{
    public static IntWrapper Factory(int value)
        =&gt; new IntWrapper(value);
}

然后,在通用代码中,您可以根据通用参数的类型调用此工厂方法:

public static implicit operator MainWrapper&lt;T, TW&gt;(T val)
    where TW : IWrapper&lt;T, TW&gt;
    =&gt; new MainWrapper&lt;T, TW&gt;{ 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&lt;T, TW&gt;
    where TW : IWrapper&lt;T, TW&gt;
{
    static abstract TW Factory(T value);
}

public struct IntWrapper : IWrapper&lt;IntWrapper, int&gt;
{
    public static IntWrapper Factory(int value)
        =&gt; 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&lt;T, TW&gt;(T val)
    where TW : IWrapper&lt;T, TW&gt;
    =&gt; new MainWrapper&lt;T, TW&gt;{ field = TW.Factory(val) };

huangapple
  • 本文由 发表于 2023年4月4日 14:36:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75926165.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定