简体   繁体   中英

Converting a generic struct to the same generic struct with different type parameter

Assuming I have a basic class hierarchy like this:

public abstract class BaseClass { }
public class X : BaseClass { }
public class Y: BaseClass { }

And I have a generic struct as this:

public struct MyStruct<T>
  where T: BaseClass, new()
{
}

I can then create an instance as follows:

var x = new MyStruct<X>();

Now i want to provide an operation (a constructor, or a conversion operator) on MyStruct , which allows me to convert MyStruct<X> to MyStruct<Y> :

MyStruct<Y> my = new MyStruct<X>();

When i write a constructor as follows:

public struct MyStruct<T>
    where T: BaseClass, new()
{
    public MyStruct(MyStruct<T2> value) 
        where T2: BaseClass, new()
    {
      ...
    }
}

The compiler does not understand what i'm trying to do (it seems it cannot distinguish between MyStruct<T> and MyStruct<T2> ).

How do i convert MyStruct<X> to MyStruct<Y> from within MyStruct<T> ?

You can't do this in the constructor, but you should be able to write a conversion method in the struct, something like:

public struct MyStruct<T>
    where T: BaseClass, new()
{
    // Convert this instance of MyStruct<T> to a new MyStruct<TOut>
    public MyStruct<TOut> ToMyStruct<TOut>()
    {
        ...
    }
}

Which would allow you to write:

struct2 = struct1.ToMyStruct<TOut>()

C# has no concept of generic conversion operators; any conversion operator, whether implicit or explicit, must either import or export a type which is fully defined by the generic parameters of the defining type. This means that Foo<T> can define a conversion operators to or from Bar<T> (and vice versa), and Foo<T,U> can define conversions to or from Bar<U> , but not vice versa. It also means that a Foo<T> can define conversions to or from `Bar, but not vice versa. Unfortunately, there is no way to have a conversion operator which can import or export a type with generic type parameters beyond those of the defining type, even if the parameters would be constrained in such a way that the cast would succeed.

What all this means is that one must often use methods rather than operators to perform conversions (or, for that matter, to perform mixed-type operations). Generic type resolution for methods is pretty smart (at least if one is willing to include a default-null dummy parameter) but for operators it's pretty limited.

Incidentally, if the only purpose for which a struct uses a generic type parameter is to define public fields of that type, it should in theory be possible for such a struct to support covariance without regard for whether the structs are "mutable", since an assignment of a KeyValuePair<FordFocus,SiameseCat> to a KeyValuePair<Vehicle,Animal> would represent the assignment of a FordFocus to a Vehicle and a SiameseCat to an Animal , both of which are type-safe. This fails, however, because all boxed structures are always mutable, and mutable aspects of a type cannot safely support covariance.

I made it work using the following approach:

public static implicit operator MyStruct<T>(MyStruct<X> value)
{
    return new MyStruct<T>(value);
}

public static implicit operator MyStruct<T>(MyStruct<Y> value)
{
    return new MyStruct<T>(value);
}

....

This is limited to known types (X, Y, ...), but lets me write the following code:

MyStruct<X> x = new MyStruct<X>(...);
MyStruct<Y> y = x;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM