Runtime: Could we have integer to tuple implicit conversion?

Created on 14 Aug 2019  路  9Comments  路  Source: dotnet/runtime

Since we have tuple it should be natural to let all integer can be construct and deconstruct as tuple of smaller type

```C#
struct Int16
{
public implicit operator short((byte,byte) bytes) => ;
public void Deconstruct(out byte x, out byte y) => ;
}

struct Int32
{
public implicit operator int((short,short) shorts) => ;
public void Deconstruct(out short x, out short y) => ;
}

struct Int64
{
public implicit operator long((int,int) ints) => ;
public void Deconstruct(out int x, out int y) => ;
}

////////

var (b0,b1) = ushort.MaxValue; // (byte,byte)
short s = (b0,b1);

var (i0,i1) = ulong.MaxValue; // (int,int)
long l = (i0,i1);

Since I think we could write it like this

```C#
int i = 123456789;
// chain implicit as (short,short) to ((byte,byte),(byte,byte)) so x,y,z,w is byte
var ((x,y),(z,w)) = i;
// same as above, expecting (short,short) should be able to chained from ((byte,byte),(byte,byte))
int j = ((w,z),(y,x));

I think we don't need to support 4 and 8 tuple cast?
But if the above is not possible I wish we could also have all

```C#
struct Int16
{
public implicit operator short((byte,byte) bytes) => ;
public void Deconstruct(out byte x, out byte y) => ;
}

struct Int32
{
public implicit operator int((short,short) shorts) => ;
public void Deconstruct(out short x, out short y) => ;

public implicit operator int((byte,byte,byte,byte) bytes) => ;
public void Deconstruct(out byte x, out byte y, out byte z, out byte w) => ;

}

struct Int64
{
public implicit operator long((int,int) ints) => ;
public void Deconstruct(out int x, out int y) => ;

public implicit operator long((short,short,short,short) shorts) => ;
public void Deconstruct(out short x, out short y, out short z, out short w) => ;

public implicit operator long((byte,byte,byte,byte,byte,byte,byte,byte) bytes) => ;
public void Deconstruct(out byte b0, out byte b1, out byte b2, out byte b3, out byte b4, out byte b5, out byte b6, out byte b7) => ;

}
```

edit:

Have been testing that compiler already understand the complex tuple deconstruction. So don't really need 4 and 8 overloading

image

image

image

api-suggestion area-System.Runtime

Most helpful comment

I do not think this should be added. If you want such functionality, it can be added via extensions. It does not belong on the corest of the core primitives.

All 9 comments

In the case where you want to deconstruct the larger value as multiple sub values you need to be explicit about endianness don't you? and there is no way to specify that here.

I also don't agree that it's natural to want to do this for anything other than serialization and that is a case where you should be very specific about your layouts so you should be using BitConverter or your own methods.

@Wraith2 I don't think so. Endianness should be default as bit of byte layout

And I think it was a common to deconstruct int from long with this code

C# long l = 123456789; int i0 = (int)(l >> 32); int i1 = (int)(l & uint.MaxValue);

Or actually it should be the same layout as BitCoverter.GetBytes

@Wraith2 And what I have using here is bitflag comparison and transferring. Also compound enum. Not about serialization at all

```C#
enum A : byte { A0,A1 //,... }
enum B : byte { B0,B1 //,... }

enum AB : short
{
A0B0 = (A0 << 8) | B0,
A0B1 = (A0 << 8) | B1,
A1B0 = (A1 << 8) | B0,
A1B1 = (A1 << 8) | B1,
//,...
}

////////////////

byte flagA,flagB;
(flagA,flagB) = AB.A0B1; // instead of (AB.A0B1 >> 8,AB.A0B1 & 0xFF)

flagB |= someValue;
var newFlag = (AB)((short)(flagA,flagB));
```

There was some more possibility. But the point is, I want to eliminate the code int i0 = (int)(l >> 32); that almost all people would use the same endian

If you think we normally use another endian in this case then we could try to analyze and use the default one instead. I am fine with any endianness. I just want the functionality to pack and unpack integer bits block

Is that not covered by BitConverter.TryWriteBytes?

@Wraith2 Why we need to use array or span when it should be fixed size bit block?

I do not think this should be added. If you want such functionality, it can be added via extensions. It does not belong on the corest of the core primitives.

@stephentoub How could implicit operator be added as extension method?

How could implicit operator be added as extension method?

It can't, nor should it be. Deconstruct can be. And you can always add an extension method that you'd use explicitly instead of an implicit cast.

As mentioned, this can be done via your own extension methods.

public static class Program {
    public static void Main() {
        (var hi, var lo) = 0x12345678;
        Console.WriteLine($"(0x{hi:X4}, 0x{lo:X4})"); // prints "(0x1234, 0x5678)"
    }
}

public static class MyExtensions
{
    public static void Deconstruct(this int i, out short hiword, out short loword)
    {
        hiword = (short)(i >> 16);
        loword = (short)i;
    }
}

Your extension method would be responsible for defining its desired endianness behaviors.

I'm closing this for now since a solution has been provided, and there's not much of an appetite by the engineering team to add these APIs. If you feel this issue has been closed in error please reopen. Thanks!

Was this page helpful?
0 / 5 - 0 ratings