7

Is there any way, how to convert this:

namespace Library
{
    public struct Content
    {
        int a;
        int b;
    }
}

I have struct in Library2.Content that has data defined same way ({ int a; int b; }), but different methods.

Is there a way to convert a struct instance from Library.Content to Library2.Content? Something like:

Library.Content c1 = new Library.Content(10, 11);
Library2.Content c2 = (Libary2.Content)(c1); //this doesn't work
strager
  • 88,763
  • 26
  • 134
  • 176
Perry
  • 113
  • 1
  • 2
  • 4

4 Answers4

13

You have several options, including:

  • You could define an explicit (or implicit) conversion operator from one type to the other. Note that this implies that one library (the one defining the conversion operator) must take a dependency on the other.
  • You could define your own utility method (possibly an extension method) that converts either type to the other. In this case, your code to do the conversion would need to change to invoke the utility method rather than performing a cast.
  • You could just new up a Library2.Content and pass in the values of your Library.Content to the constructor.
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • What if you can't because you have no access to the constructor or some types of the other? This is something that I require in order to create Unit Tests but sadly the library doesn't let me create my own scruct instances. – Darkgaze Jul 19 '21 at 07:45
9

Just for completeness, there is another way to do this if the layout of the data types is the same - through marshaling.

static void Main(string[] args)
{

    foo1 s1 = new foo1();
    foo2 s2 = new foo2();
    s1.a = 1;
    s1.b = 2;

    s2.c = 3;
    s2.d = 4;

    object s3 = s1;
    s2 = CopyStruct<foo2>(ref s3);

}

static T CopyStruct<T>(ref object s1)
{
    GCHandle handle = GCHandle.Alloc(s1, GCHandleType.Pinned);
    T typedStruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();
    return typedStruct;
}

struct foo1
{
    public int a;
    public int b;

    public void method1() { Console.WriteLine("foo1"); }
}

struct foo2
{
    public int c;
    public int d;

    public void method2() { Console.WriteLine("foo2"); }
}
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • 5
    Similarly if you allow unsafe code: `foo2 s2 = *(foo2*)&s1;` – C.Evenhuis Sep 26 '17 at 11:25
  • Haha, I was just going to write literally the same thing :) – nurchi Sep 15 '18 at 05:23
  • @C.Evenhuis I couldn't do this. Got the message: Cannot take the address of, get the size of, or declare a pointer to a managed type. My foo2 is equal to foo1, and I have no access to foo1, so I wanted to do my "unsafe" conversion to that type. – Darkgaze Jul 19 '21 at 07:44
  • @Darkgaze that trick only works for structs, and if you're using generics I think you need a `where T : unmanaged` constraint. – C.Evenhuis Jul 19 '21 at 08:26
  • @C.Evenhuis The problem was that it contained pointers to classes. :-/ So yes, not a ideal case. – Darkgaze Jul 23 '21 at 07:41
5

You could define an explicit conversion operator inside Library2.Content as follows:

// explicit Library.Content to Library2.Content conversion operator
public static explicit operator Content(Library.Content content) {
    return new Library2.Content {
       a = content.a,
       b = content.b
    };
}
Bertrand Marron
  • 21,501
  • 8
  • 58
  • 94
0

A bit late to the game. But if the structs match I used this in the past

public static object CastTo(ref object obj, Type type)
{
    var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(type));
    try
    {
        Marshal.StructureToPtr(obj, ptr, false);
        return Marshal.PtrToStructure(ptr, type);
    }
    finally
    {
        Marshal.FreeHGlobal(ptr);
    }
}

Thanks to @avi_sweden for pointing out that the allocated memory should be freed

SWSBB
  • 61
  • 5