How to call MemoryMarshal.CreateSpan using Reflection API? - c#

Let's suppose I have this C# structure:
public struct Test
{
public int Code1;
public int Code2;
}
This code works fine:
var test = new Test { Code1 = 1, Code2 = 2 };
var bytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref test, 1));
In bytes, I will get an array of 8 bytes (1, 0, 0, 0, 2, 0, 0, 0).
Now, how can I call the same methods CreateSpan + AsBytes using Reflection API instead of a predefined/generic type?
I have tried this for the CreateSpan part:
private static object GetBytes(object value)
{
var method = typeof(MemoryMarshal).GetMethod(nameof(MemoryMarshal.CreateSpan));
var gmethod = method.MakeGenericMethod(value.GetType());
var parameters = new object[] { value, 1 };
return gmethod.Invoke(null, parameters);
}
But on Invoke (using the same test instance), it says: System.NotSupportedException: 'Specified method is not supported.'
PS: I would like to avoid pointers and unsafe keyword.

You cannot access Span<T> using reflection because it cannot be boxed (to prevent stack references escaping).
The only thing you can do is create a generic function that does all you want it to do, then call that using reflection.
For example, you could return a byte[] array out of a generic function
private static byte[] GetBytes(object value)
{
var method = typeof(ThisType).GetMethod("GetByteArray", BindingFlags.Static | BindingFlags.NonPublic);
var gmethod = method.MakeGenericMethod(value.GetType());
return (byte[])gmethod.Invoke(null, new object[] { value });
}
private static byte[] GetByteArray<T>(T value) where T : struct
{
return MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan<T>(ref value, 1)).ToArray();
}
dotnetfiddle

Related

How to provide int[,] and int values to test method, C#

I need this method for unit testing. There is matrix and method that calculate matrix trace which is integer. So i need to provide simple matrix (int[,]) and expected trace:
[DynamicData(nameof(TestDataMethod), DynamicDataSourceType.Method)]
public void TestReturnTrace(int[,] simpleMatrix, int expected)
{
var mock = new Mock<IMatrixGenerator>();
mock.Setup(a => a.GenerateRandomMatrix()).Returns(simpleMatrix);
MatrixBase matrix = new MatrixBase(mock.Object);
int actual = matrix.ReturnTrace();
Assert.AreEqual(expected, actual);
}
How can i create simple 2D array and integer value and return it?
My best attempt is:
static IEnumerable<object[], int> TestDataMethod()
{
int[,] array2d = new int[,] { { 1, 0 }, { 0, 1 } };
int myInteger = 2;
return (new[] {
new[] { array2d },
myInteger )
};
}
but the error is "No best type found for implicitly-typed array", like if I try to create array, but I just pair two values in parentheses. Where am I wrong?
Your return type doesn't match the method data, you can use tuple (int[,], int) as generic type parameter for IEnumerable<T> and yield return to return a single item.
static IEnumerable<(int[,], int)> TestDataMethod()
{
int[,] array2d = { { 1, 0 }, { 0, 1 } };
int myInteger = 2;
yield return (array2d, myInteger);
}
Update: just looked at some articles regarding MSTest and DynamicData attribute, like this. The correct approach seems to be using an IEnumerable<object[]>, where every item in object array represents a single argument in test method
static IEnumerable<object[]> TestDataMethod()
{
int[,] array2d = { { 1, 0 }, { 0, 1 } };
int myInteger = 2;
yield return new object[] { array2d, myInteger };
}

How to make a compound datatype with HDF5DOTNET?

I have problems when I write a struct with arrays in it into an HDF5 dataset. Firstly, the window form doesn't start with the line:
H5T.insert(typeStruct, "string", 0, H5T.create_array(new H5DataTypeId(H5T.H5Type.C_S1), dims2));
The window form at least starts without the line, so I think there's something wrong with defining the compound datatype. I've looked into manuals and many examples, but I can't still fix the problems. Could I get an example of using compound datatypes to write a struct with multiple arrays in C#?
using HDF5DotNet;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
namespace WindowsFormsApplication1
{
public unsafe partial class Form1 : Form
{
public unsafe struct struct_TR
{
public string[] arr_currentLong;
public struct_TR(byte size_currentTime)
{
arr_currentLong = new string[size_currentTime];
}
}
public Form1()
{
InitializeComponent();
long ARRAY_SIZE = 255;
struct_TR structMade = new struct_TR(255);
for (int i = 0; i < 255; i++)
{
structMade.arr_currentLong[i] = i.ToString();
}
string currentPath = Path.GetDirectoryName(Application.ExecutablePath);
Directory.SetCurrentDirectory(currentPath);
H5FileId fileId = H5F.create(#"weights.h5", H5F.CreateMode.ACC_TRUNC);
long[] dims1 = { 1 };
long[] dims2 = { 1, ARRAY_SIZE };
H5DataSpaceId myDataSpace = H5S.create_simple(1, dims1);
H5DataTypeId string_type = H5T.copy(H5T.H5Type.C_S1);
H5DataTypeId array_tid1 = H5T.create_array(string_type, dims2);
H5DataTypeId typeStruct = H5T.create(H5T.CreateClass.COMPOUND, Marshal.SizeOf(typeof(struct_TR)));
H5T.insert(typeStruct, "string", 0, H5T.create_array(new H5DataTypeId(H5T.H5Type.C_S1), dims2));
H5DataSetId myDataSet = H5D.create(fileId, "/dset", typeStruct, myDataSpace);
H5D.writeScalar<struct_TR>(myDataSet, typeStruct, ref structMade);
}
}
}
the only way I know how to save structs with arrays is to create an array that is constant
So for example this is a struct with an array of length 4.
[StructLayout(LayoutKind.Sequential)]
public struct Responses
{
public Int64 MCID;
public int PanelIdx;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public short[] ResponseValues;
}
Here an array of 4 structs containing an array is created:
responseList = new Responses[4] {
new Responses() { MCID=1,PanelIdx=5,ResponseValues=new short[4]{ 1,2,3,4} },
new Responses() { MCID=2,PanelIdx=6,ResponseValues=new short[4]{ 5,6,7,8}},
new Responses() { MCID=3,PanelIdx=7,ResponseValues=new short[4]{ 1,2,3,4}},
new Responses() { MCID=4,PanelIdx=8,ResponseValues=new short[4]{ 5,6,7,8}}
};
The following lines of code write an array of structs to a HDF5 file:
string filename = "testArrayCompounds.H5";
var fileId =H5F.create(filename, H5F.ACC_TRUNC);
var status = WriteCompounds(fileId, "/test", responseList);
H5F.close(fileId);
The WriteCompounds method looks like this:
public static int WriteCompounds<T>(hid_t groupId, string name, IEnumerable<T> list) //where T : struct
{
Type type = typeof(T);
var size = Marshal.SizeOf(type);
var cnt = list.Count();
var typeId = CreateType(type);
var log10 = (int)Math.Log10(cnt);
ulong pow = (ulong)Math.Pow(10, log10);
ulong c_s = Math.Min(1000, pow);
ulong[] chunk_size = new ulong[] { c_s };
ulong[] dims = new ulong[] { (ulong)cnt };
long dcpl = 0;
if (!list.Any() || log10 == 0) { }
else
{
dcpl = CreateProperty(chunk_size);
}
// Create dataspace. Setting maximum size to NULL sets the maximum
// size to be the current size.
var spaceId = H5S.create_simple(dims.Length, dims, null);
// Create the dataset and write the compound data to it.
var datasetId = H5D.create(groupId, name, typeId, spaceId, H5P.DEFAULT, dcpl);
IntPtr p = Marshal.AllocHGlobal(size * (int)dims[0]);
var ms = new MemoryStream();
BinaryWriter writer = new BinaryWriter(ms);
foreach (var strct in list)
writer.Write(getBytes(strct));
var bytes = ms.ToArray();
GCHandle hnd = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var statusId = H5D.write(datasetId, typeId, spaceId, H5S.ALL,
H5P.DEFAULT, hnd.AddrOfPinnedObject());
hnd.Free();
/*
* Close and release resources.
*/
H5D.close(datasetId);
H5S.close(spaceId);
H5T.close(typeId);
H5P.close(dcpl);
Marshal.FreeHGlobal(p);
return statusId;
}
Three additional help functions are needed, two are shown here:
private static long CreateType(Type t)
{
var size = Marshal.SizeOf(t);
var float_size = Marshal.SizeOf(typeof(float));
var int_size = Marshal.SizeOf(typeof(int));
var typeId = H5T.create(H5T.class_t.COMPOUND, new IntPtr(size));
var compoundInfo = Hdf5.GetCompoundInfo(t);
foreach (var cmp in compoundInfo)
{
H5T.insert(typeId, cmp.name, Marshal.OffsetOf(t, cmp.name), cmp.datatype);
}
return typeId;
}
private static long CreateProperty(ulong[] chunk_size)
{
var dcpl = H5P.create(H5P.DATASET_CREATE);
H5P.set_layout(dcpl, H5D.layout_t.CHUNKED);
H5P.set_chunk(dcpl, chunk_size.Length, chunk_size);
H5P.set_deflate(dcpl, 6);
return dcpl;
}
I also have a ReadCompounds to read the hdf5 file. The Hdf5.GetCompoundInfo method used in the CreateType method is also very long. So I won't show these methods here.
So that's quite a lot of code just for writing some structs.
I have made a library called HDF5DotnetTools that allows you to read and write classes and structs much more easily. There you can also find the ReadCompounds and GetCompoundInfo methods.
In the unit tests of the HDF5DotnetTools you can also find examples of how to write classes with arrays

Cast Int Array to Enum Flags

I have the following enum with flags:
[Flags]
public enum DataFiat {
Public = 1,
Listed = 2,
Client = 4
} // DataFiat
And I have an int array, for example:
int[] selected = new int[] { 1, 4 }
How can I convert this to my enum which would become:
DataFiat.Public | DataFiat.Client
Thank You,
Miguel
var f = (DataFiat)selected.Sum();
How about something like
var tt = (DataFiat)selected.Aggregate((i, t) => i | t);
this snippet:
var intArr = new[] { 1, 4 };
var sum = intArr.Sum(x => x);
var result = (Test)sum;
returns
DataFlat result = (DataFlat) 0;
foreach (var value in selected)
{
result |= (DataFlat)value;
}
Or if you want to use LINQ
DataFlat result = (DataFlat) selected.Aggregate(0, (old, current) => old | current);
You mean this?
IEnumerable<DataFiat> selectedDataFiats = selected.Cast<DataFiat>();
This sinmply casts each int to DataFiat.
You can't just cast the array, if it's really an object[]. You can create a new array pretty easily though:
var enumArray = originalArray.Cast<DataFiat>().ToArray();
If it were actually an int[] array to start with, you could cast - although you'd have to talk nicely to the C# compiler first:
using System;
class Program
{
enum Foo
{
Bar = 1,
Baz = 2
}
static void Main()
{
int[] ints = new int[] { 1, 2 };
Foo[] foos = (Foo[]) (object) ints;
foreach (var foo in foos)
{
Console.WriteLine(foo);
}
}
}
The C# compiler doesn't believe that there's a conversion from int[] to Foo[] (and there isn't, within the rules of C#)... but the CLR is fine with this conversion, so as long as you can persuade the C# compiler to play along (by casting to object first) it's fine.
This doesn't work when the original array is really an object[] though.
Hope this helps..

Serialize C# Enum Definition to Json

Given the following in C#:
[Flags]
public enum MyFlags {
None = 0,
First = 1 << 0,
Second = 1 << 1,
Third = 1 << 2,
Fourth = 1 << 3
}
Are there any existing methods in ServiceStack.Text for serializing to the following JSON?
{
"MyFlags": {
"None": 0,
"First": 1,
"Second": 2,
"Third": 4,
"Fourth": 8
}
}
Currently I'm using the routine below, are there better ways to do this?
public static string ToJson(this Type type)
{
var stringBuilder = new StringBuilder();
Array values = Enum.GetValues(type);
stringBuilder.Append(string.Format(#"{{ ""{0}"": {{", type.Name));
foreach (Enum value in values)
{
stringBuilder.Append(
string.Format(
#"""{0}"": {1},",
Enum.GetName(typeof(Highlights), value),
Convert.ChangeType(value, value.GetTypeCode())));
}
stringBuilder.Remove(stringBuilder.Length - 1, 1);
stringBuilder.Append("}}");
return stringBuilder.ToString();
}
public static class EnumExtensions
{
public static string EnumToJson(this Type type)
{
if (!type.IsEnum)
throw new InvalidOperationException("enum expected");
var results =
Enum.GetValues(type).Cast<object>()
.ToDictionary(enumValue => enumValue.ToString(), enumValue => (int) enumValue);
return string.Format("{{ \"{0}\" : {1} }}", type.Name, Newtonsoft.Json.JsonConvert.SerializeObject(results));
}
}
Using a dictionary of to do the heavy lifting. Then using Newtonsoft's json convert to convert that to json. I just had to do a bit of wrapping to add the type name on.
You're better off populating a Dictionary<string,int> or a Typed DTO and serializing that.

Can a variant array be passed to C# using com interop?

I try to transfer some data between Excel and C#. For this I wrote a simple C# class with a property to set and get the data.
[Guid("xxx")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Vector
{
private object[,] _values;
public object[,] ExcelValues
{
get { ... }
set { ... }
}
}
Getting the ExcelValues property in VBA works well, but it is not possible to set it in VBA. The VBA code does not compile if I try to set the property:
Dim values As Variant
With myRange
' typo: Set values = Range(.Offset(0, 0), .Offset(100, 0))
values = Range(.Offset(0, 0), .Offset(100, 0))
End With
Dim data As New Vector
' this doesn't compile
data.ExcelValues = values
' this works
values = data.ExcelValues
Any suggestions how I can acomplish this, without setting each value from the variant Array one at a time?
I found a solution based on code that was posted here. A variant array has to be passed from VBA to C# as an object (not object[,]). Then it can be converted to something that is more handy by using reflection:
[Guid("xxx")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Vector
{
[ComVisible(false)]
public IList<double> Values { get; set; }
public object[,] GetExcelValues()
{
// own extension method
return Values.ConvertToExcelColumn();
}
public void SetExcelValues(object comArray)
{
IEnumerable<object> values = ConvertExcelCloumnToEnumerable(comArray);
Values = new List<double>(values.Select(Convert.ToDouble));
}
private static IEnumerable<object> ConvertExcelCloumnToEnumerable(object comObject)
{
Type comObjectType = comObject.GetType();
if (comObjectType != typeof(object[,]))
return new object[0];
int count = (int)comObjectType.InvokeMember("Length", BindingFlags.GetProperty, null, comObject, null);
var result = new List<object>(count);
var indexArgs = new object[2];
for (int i = 1; i <= count; i++)
{
indexArgs[0] = i;
indexArgs[1] = 1;
object valueAtIndex = comObjectType.InvokeMember("GetValue", BindingFlags.InvokeMethod, null, comObject, indexArgs);
result.Add(valueAtIndex);
}
return result;
}
}
The other way - from C# to VBA - it can be passed more comfortable as object[,] or double[,].
Hope there are no syntax typos :).
By using Set you're telling VBA that "values" is an object (in this case a Range), but then you're not using set when assigning that to ExcelValues. Try dropping the Set when you're reading the values.
With myRange
values = Range(.Offset(0, 0), .Offset(100, 0)).Value
End With

Categories