I am trying to print out the contents of an array after invoking some methods which alter it, in Java I use:
System.out.print(Arrays.toString(alg.id));
how do I do this in c#?
You may try this:
foreach(var item in yourArray)
{
Console.WriteLine(item.ToString());
}
Also you may want to try something like this:
yourArray.ToList().ForEach(i => Console.WriteLine(i.ToString()));
EDIT: to get output in one line [based on your comment]:
Console.WriteLine("[{0}]", string.Join(", ", yourArray));
//output style: [8, 1, 8, 8, 4, 8, 6, 8, 8, 8]
EDIT(2019): As it is mentioned in other answers it is better to use Array.ForEach<T> method and there is no need to do the ToList step.
Array.ForEach(yourArray, Console.WriteLine);
There are many ways to do it, the other answers are good, here's an alternative:
Console.WriteLine(string.Join("\n", myArrayOfObjects));
The easiest one e.g. if you have a string array declared like this string[] myStringArray = new string[];
Console.WriteLine("Array : ");
Console.WriteLine("[{0}]", string.Join(", ", myStringArray));
I decided to test the speeds of the different methods posted here:
These are the four methods I used.
static void Print1(string[] toPrint)
{
foreach(string s in toPrint)
{
Console.Write(s);
}
}
static void Print2(string[] toPrint)
{
toPrint.ToList().ForEach(Console.Write);
}
static void Print3(string[] toPrint)
{
Console.WriteLine(string.Join("", toPrint));
}
static void Print4(string[] toPrint)
{
Array.ForEach(toPrint, Console.Write);
}
The results are as follows:
Strings per trial: 10000
Number of Trials: 100
Total Time Taken to complete: 00:01:20.5004836
Print1 Average: 484.37ms
Print2 Average: 246.29ms
Print3 Average: 70.57ms
Print4 Average: 233.81ms
So Print3 is the fastest, because it only has one call to the Console.WriteLine which seems to be the main bottleneck for the speed of printing out an array. Print4 is slightly faster than Print2 and Print1 is the slowest of them all.
I think that Print4 is probably the most versatile of the 4 I tested, even though Print3 is faster.
If I made any errors, feel free to let me know / fix them on your own!
EDIT: I'm adding the generated IL below
g__Print10_0://Print1
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: stloc.1
IL_0004: br.s IL_0012
IL_0006: ldloc.0
IL_0007: ldloc.1
IL_0008: ldelem.ref
IL_0009: call System.Console.Write
IL_000E: ldloc.1
IL_000F: ldc.i4.1
IL_0010: add
IL_0011: stloc.1
IL_0012: ldloc.1
IL_0013: ldloc.0
IL_0014: ldlen
IL_0015: conv.i4
IL_0016: blt.s IL_0006
IL_0018: ret
g__Print20_1://Print2
IL_0000: ldarg.0
IL_0001: call System.Linq.Enumerable.ToList<String>
IL_0006: ldnull
IL_0007: ldftn System.Console.Write
IL_000D: newobj System.Action<System.String>..ctor
IL_0012: callvirt System.Collections.Generic.List<System.String>.ForEach
IL_0017: ret
g__Print30_2://Print3
IL_0000: ldstr ""
IL_0005: ldarg.0
IL_0006: call System.String.Join
IL_000B: call System.Console.WriteLine
IL_0010: ret
g__Print40_3://Print4
IL_0000: ldarg.0
IL_0001: ldnull
IL_0002: ldftn System.Console.Write
IL_0008: newobj System.Action<System.String>..ctor
IL_000D: call System.Array.ForEach<String>
IL_0012: ret
Another approach with the Array.ForEach<T> Method (T[], Action<T>) method of the Array class
Array.ForEach(myArray, Console.WriteLine);
That takes only one iteration compared to array.ToList().ForEach(Console.WriteLine) which takes two iterations and creates internally a second array for the List (double iteration runtime and double memory consumtion)
Starting from C# 6.0, when $ - string interpolation was introduced, there is one more way:
var array = new[] { "A", "B", "C" };
Console.WriteLine($"{string.Join(", ", array)}");
//output
A, B, C
Concatenation could be archived using the System.Linq, convert the string[] to char[] and print as a string:
var array = new[] { "A", "B", "C" };
Console.WriteLine($"{new String(array.SelectMany(_ => _).ToArray())}");
//output
ABC
In C# you can loop through the array printing each element. Note that System.Object defines a method ToString(). Any given type that derives from System.Object() can override that.
Returns a string that represents the current object.
http://msdn.microsoft.com/en-us/library/system.object.tostring.aspx
By default the full type name of the object will be printed, though many built-in types override that default to print a more meaningful result. You can override ToString() in your own objects to provide meaningful output.
foreach (var item in myArray)
{
Console.WriteLine(item.ToString()); // Assumes a console application
}
If you had your own class Foo, you could override ToString() like:
public class Foo
{
public override string ToString()
{
return "This is a formatted specific for the class Foo.";
}
}
If you want to get cute, you could write an extension method that wrote an IEnumerable<object> sequence to the console. This will work with enumerables of any type, because IEnumerable<T> is covariant on T:
using System;
using System.Collections.Generic;
namespace Demo
{
internal static class Program
{
private static void Main(string[] args)
{
string[] array = new []{"One", "Two", "Three", "Four"};
array.Print();
Console.WriteLine();
object[] objArray = new object[] {"One", 2, 3.3, TimeSpan.FromDays(4), '5', 6.6f, 7.7m};
objArray.Print();
}
}
public static class MyEnumerableExt
{
public static void Print(this IEnumerable<object> #this)
{
foreach (var obj in #this)
Console.WriteLine(obj);
}
}
}
(I don't think you'd use this other than in test code.)
I upvoted the extension method answer by Matthew Watson, but if you're migrating/visiting coming from Python, you may find such a method useful:
class Utils
{
static void dump<T>(IEnumerable<T> list, string glue="\n")
{
Console.WriteLine(string.Join(glue, list.Select(x => x.ToString())));
}
}
-> this will print any collection using the separator provided. It's quite limited (nested collections?).
For a script (i.e. a C# console application which only contains Program.cs, and most things happen in Program.Main) - this may be just fine.
this is the easiest way that you could print the String by using array!!!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace arraypracticeforstring
{
class Program
{
static void Main(string[] args)
{
string[] arr = new string[3] { "Snehal", "Janki", "Thakkar" };
foreach (string item in arr)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
}
}
If it's an array of strings you can use Aggregate
var array = new string[] { "A", "B", "C", "D"};
Console.WriteLine(array.Aggregate((result, next) => $"{result}, {next}")); // A, B, C, D
that way you can reverses the order by changing the order of the parameters like so
Console.WriteLine(array.Aggregate((result, next) => $"{next}, {result}")); // D, C, B, A
You can use for loop
int[] random_numbers = {10, 30, 44, 21, 51, 21, 61, 24, 14}
int array_length = random_numbers.Length;
for (int i = 0; i < array_length; i++){
if(i == array_length - 1){
Console.Write($"{random_numbers[i]}\n");
} else{
Console.Write($"{random_numbers[i]}, ");
}
}
If you do not want to use the Array function.
public class GArray
{
int[] mainArray;
int index;
int i = 0;
public GArray()
{
index = 0;
mainArray = new int[4];
}
public void add(int addValue)
{
if (index == mainArray.Length)
{
int newSize = index * 2;
int[] temp = new int[newSize];
for (int i = 0; i < mainArray.Length; i++)
{
temp[i] = mainArray[i];
}
mainArray = temp;
}
mainArray[index] = addValue;
index++;
}
public void print()
{
for (int i = 0; i < index; i++)
{
Console.WriteLine(mainArray[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
GArray myArray = new GArray();
myArray.add(1);
myArray.add(2);
myArray.add(3);
myArray.add(4);
myArray.add(5);
myArray.add(6);
myArray.print();
Console.ReadKey();
}
}
Related
I am trying to print out the contents of an array after invoking some methods which alter it, in Java I use:
System.out.print(Arrays.toString(alg.id));
how do I do this in c#?
You may try this:
foreach(var item in yourArray)
{
Console.WriteLine(item.ToString());
}
Also you may want to try something like this:
yourArray.ToList().ForEach(i => Console.WriteLine(i.ToString()));
EDIT: to get output in one line [based on your comment]:
Console.WriteLine("[{0}]", string.Join(", ", yourArray));
//output style: [8, 1, 8, 8, 4, 8, 6, 8, 8, 8]
EDIT(2019): As it is mentioned in other answers it is better to use Array.ForEach<T> method and there is no need to do the ToList step.
Array.ForEach(yourArray, Console.WriteLine);
There are many ways to do it, the other answers are good, here's an alternative:
Console.WriteLine(string.Join("\n", myArrayOfObjects));
The easiest one e.g. if you have a string array declared like this string[] myStringArray = new string[];
Console.WriteLine("Array : ");
Console.WriteLine("[{0}]", string.Join(", ", myStringArray));
I decided to test the speeds of the different methods posted here:
These are the four methods I used.
static void Print1(string[] toPrint)
{
foreach(string s in toPrint)
{
Console.Write(s);
}
}
static void Print2(string[] toPrint)
{
toPrint.ToList().ForEach(Console.Write);
}
static void Print3(string[] toPrint)
{
Console.WriteLine(string.Join("", toPrint));
}
static void Print4(string[] toPrint)
{
Array.ForEach(toPrint, Console.Write);
}
The results are as follows:
Strings per trial: 10000
Number of Trials: 100
Total Time Taken to complete: 00:01:20.5004836
Print1 Average: 484.37ms
Print2 Average: 246.29ms
Print3 Average: 70.57ms
Print4 Average: 233.81ms
So Print3 is the fastest, because it only has one call to the Console.WriteLine which seems to be the main bottleneck for the speed of printing out an array. Print4 is slightly faster than Print2 and Print1 is the slowest of them all.
I think that Print4 is probably the most versatile of the 4 I tested, even though Print3 is faster.
If I made any errors, feel free to let me know / fix them on your own!
EDIT: I'm adding the generated IL below
g__Print10_0://Print1
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: stloc.1
IL_0004: br.s IL_0012
IL_0006: ldloc.0
IL_0007: ldloc.1
IL_0008: ldelem.ref
IL_0009: call System.Console.Write
IL_000E: ldloc.1
IL_000F: ldc.i4.1
IL_0010: add
IL_0011: stloc.1
IL_0012: ldloc.1
IL_0013: ldloc.0
IL_0014: ldlen
IL_0015: conv.i4
IL_0016: blt.s IL_0006
IL_0018: ret
g__Print20_1://Print2
IL_0000: ldarg.0
IL_0001: call System.Linq.Enumerable.ToList<String>
IL_0006: ldnull
IL_0007: ldftn System.Console.Write
IL_000D: newobj System.Action<System.String>..ctor
IL_0012: callvirt System.Collections.Generic.List<System.String>.ForEach
IL_0017: ret
g__Print30_2://Print3
IL_0000: ldstr ""
IL_0005: ldarg.0
IL_0006: call System.String.Join
IL_000B: call System.Console.WriteLine
IL_0010: ret
g__Print40_3://Print4
IL_0000: ldarg.0
IL_0001: ldnull
IL_0002: ldftn System.Console.Write
IL_0008: newobj System.Action<System.String>..ctor
IL_000D: call System.Array.ForEach<String>
IL_0012: ret
Another approach with the Array.ForEach<T> Method (T[], Action<T>) method of the Array class
Array.ForEach(myArray, Console.WriteLine);
That takes only one iteration compared to array.ToList().ForEach(Console.WriteLine) which takes two iterations and creates internally a second array for the List (double iteration runtime and double memory consumtion)
Starting from C# 6.0, when $ - string interpolation was introduced, there is one more way:
var array = new[] { "A", "B", "C" };
Console.WriteLine($"{string.Join(", ", array)}");
//output
A, B, C
Concatenation could be archived using the System.Linq, convert the string[] to char[] and print as a string:
var array = new[] { "A", "B", "C" };
Console.WriteLine($"{new String(array.SelectMany(_ => _).ToArray())}");
//output
ABC
In C# you can loop through the array printing each element. Note that System.Object defines a method ToString(). Any given type that derives from System.Object() can override that.
Returns a string that represents the current object.
http://msdn.microsoft.com/en-us/library/system.object.tostring.aspx
By default the full type name of the object will be printed, though many built-in types override that default to print a more meaningful result. You can override ToString() in your own objects to provide meaningful output.
foreach (var item in myArray)
{
Console.WriteLine(item.ToString()); // Assumes a console application
}
If you had your own class Foo, you could override ToString() like:
public class Foo
{
public override string ToString()
{
return "This is a formatted specific for the class Foo.";
}
}
If you want to get cute, you could write an extension method that wrote an IEnumerable<object> sequence to the console. This will work with enumerables of any type, because IEnumerable<T> is covariant on T:
using System;
using System.Collections.Generic;
namespace Demo
{
internal static class Program
{
private static void Main(string[] args)
{
string[] array = new []{"One", "Two", "Three", "Four"};
array.Print();
Console.WriteLine();
object[] objArray = new object[] {"One", 2, 3.3, TimeSpan.FromDays(4), '5', 6.6f, 7.7m};
objArray.Print();
}
}
public static class MyEnumerableExt
{
public static void Print(this IEnumerable<object> #this)
{
foreach (var obj in #this)
Console.WriteLine(obj);
}
}
}
(I don't think you'd use this other than in test code.)
I upvoted the extension method answer by Matthew Watson, but if you're migrating/visiting coming from Python, you may find such a method useful:
class Utils
{
static void dump<T>(IEnumerable<T> list, string glue="\n")
{
Console.WriteLine(string.Join(glue, list.Select(x => x.ToString())));
}
}
-> this will print any collection using the separator provided. It's quite limited (nested collections?).
For a script (i.e. a C# console application which only contains Program.cs, and most things happen in Program.Main) - this may be just fine.
this is the easiest way that you could print the String by using array!!!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace arraypracticeforstring
{
class Program
{
static void Main(string[] args)
{
string[] arr = new string[3] { "Snehal", "Janki", "Thakkar" };
foreach (string item in arr)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
}
}
If it's an array of strings you can use Aggregate
var array = new string[] { "A", "B", "C", "D"};
Console.WriteLine(array.Aggregate((result, next) => $"{result}, {next}")); // A, B, C, D
that way you can reverses the order by changing the order of the parameters like so
Console.WriteLine(array.Aggregate((result, next) => $"{next}, {result}")); // D, C, B, A
You can use for loop
int[] random_numbers = {10, 30, 44, 21, 51, 21, 61, 24, 14}
int array_length = random_numbers.Length;
for (int i = 0; i < array_length; i++){
if(i == array_length - 1){
Console.Write($"{random_numbers[i]}\n");
} else{
Console.Write($"{random_numbers[i]}, ");
}
}
If you do not want to use the Array function.
public class GArray
{
int[] mainArray;
int index;
int i = 0;
public GArray()
{
index = 0;
mainArray = new int[4];
}
public void add(int addValue)
{
if (index == mainArray.Length)
{
int newSize = index * 2;
int[] temp = new int[newSize];
for (int i = 0; i < mainArray.Length; i++)
{
temp[i] = mainArray[i];
}
mainArray = temp;
}
mainArray[index] = addValue;
index++;
}
public void print()
{
for (int i = 0; i < index; i++)
{
Console.WriteLine(mainArray[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
GArray myArray = new GArray();
myArray.add(1);
myArray.add(2);
myArray.add(3);
myArray.add(4);
myArray.add(5);
myArray.add(6);
myArray.print();
Console.ReadKey();
}
}
I would like to know what is the value of an out/ref parameter of a invoked method.
When the method is invoked without throws an exception, the value is received in the parameter, but I do not get the value when an exception throws in the invoked method. Invoking directly the method without Reflection, the value is received.
Am I doing something wrong or is this a .net limitation?
using System;
using System.Reflection;
class Program
{
static void Main()
{
string[] arguments = new string[] { bool.FalseString, null };
MethodInfo method = typeof(Program).GetMethod("SampleMethod");
try
{
method.Invoke(null, arguments);
Console.WriteLine(arguments[1]); // arguments[1] = "Hello", Prints Hello
arguments = new string[] { bool.TrueString, null };
method.Invoke(null, arguments);
}
catch (Exception)
{
Console.WriteLine(arguments[1]); // arguments[1] = null, Does not print
}
arguments[1] = null;
try
{
SampleMethod(bool.TrueString, out arguments[1]);
}
catch (Exception)
{
Console.WriteLine(arguments[1]); // arguments[1] = "Hello"
}
}
public static void SampleMethod(string throwsException, out string text)
{
text = "Hello";
if (throwsException == bool.TrueString)
throw new Exception("Test Exception");
}
}
After search a litlle bit I found the solution below. Would be good to use it?
using System;
using System.Reflection;
using System.Reflection.Emit;
public static class MethodInfoExtension
{
public static object InvokeStrictly(this MethodInfo source, object obj, object[] parameters)
{
ParameterInfo[] paramInfos = source.GetParameters();
if ((parameters == null) || (paramInfos.Length != parameters.Length))
{
throw new ArgumentException();
}
Type[] paramTypes = new[] { typeof(object[]) };
DynamicMethod invokerBuilder = new DynamicMethod(string.Empty, typeof(object), paramTypes);
ILGenerator ilGenerator = invokerBuilder.GetILGenerator();
Label exBlockLabel = ilGenerator.BeginExceptionBlock();
for (int i = 0; i < paramInfos.Length; i++)
{
var paramInfo = paramInfos[i];
bool paramIsByRef = paramInfo.ParameterType.IsByRef;
var paramType = paramIsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType;
ilGenerator.DeclareLocal(paramType);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldelem_Ref);
Label label1 = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Brfalse, label1);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldelem_Ref);
ilGenerator.Emit(OpCodes.Unbox_Any, paramType);
ilGenerator.Emit(OpCodes.Stloc_S, (byte)i);
ilGenerator.MarkLabel(label1);
if (paramIsByRef)
{
ilGenerator.Emit(OpCodes.Ldloca_S, (byte)i);
}
else
{
ilGenerator.Emit(OpCodes.Ldloc_S, (byte)i);
}
}
LocalBuilder resultLocal = ilGenerator.DeclareLocal(typeof(object), false);
ilGenerator.Emit(OpCodes.Call, source);
if (source.ReturnType == typeof(void))
{
ilGenerator.Emit(OpCodes.Ldnull);
}
ilGenerator.Emit(OpCodes.Stloc_S, resultLocal);
ilGenerator.Emit(OpCodes.Leave, exBlockLabel);
ilGenerator.BeginFinallyBlock();
for (int i = 0; i < paramInfos.Length; i++)
{
var paramInfo = paramInfos[i];
bool paramIsByRef = paramInfo.ParameterType.IsByRef;
var paramType = paramIsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType;
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldloc_S, (byte)i);
if (paramType.IsValueType)
{
ilGenerator.Emit(OpCodes.Box, paramType);
}
ilGenerator.Emit(OpCodes.Stelem, typeof(object));
}
ilGenerator.EndExceptionBlock();
ilGenerator.Emit(OpCodes.Ldloc_S, resultLocal);
ilGenerator.Emit(OpCodes.Ret);
var invoker = (Func<object[], object>)invokerBuilder.CreateDelegate(typeof(Func<object[], object>));
return invoker(parameters);
}
}
public class Program
{
static void Main()
{
object[] args = new object[1];
try
{
MethodInfo targetMethod = typeof(Program).GetMethod("Method");
targetMethod.InvokeStrictly(null, args);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine();
}
Console.WriteLine(args[0]);
Console.ReadLine();
}
public static void Method(out string arg)
{
arg = "Hello";
throw new Exception("Test Exception");
}
}
In short, you are not doing anything wrong. Its a limitation on the implementation on invoke.
When using ref in a direct call the reference of your local value gets passed into the method. With invoke, for security reasons, a copy is made and copied back into your local reference only if the call did not throw an exception.
For the long answer...
So, taken your example I created this fiddle to view the IL code. That gives us the following:
.method public hidebysig static void SampleMethod(string throwsException, [out] string& text) cil managed
{
//
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1 // Get argument 2
IL_0002: ldstr "Hello" // Get string literal
IL_0007: stind.ref // store in reference address
IL_0008: ldarg.0
IL_0009: ldsfld string [mscorlib]System.Boolean::TrueString
IL_000e: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0013: ldc.i4.0
IL_0014: ceq
IL_0016: stloc.0
IL_0017: ldloc.0
IL_0018: brtrue.s IL_0025
IL_001a: ldstr "Test Exception"
IL_001f: newobj instance void [mscorlib]System.Exception::.ctor(string)
IL_0024: throw
IL_0025: ret
} // end of method Program::SampleMethod
As expected the value of "Hello" gets set in the reference address of the second (output) parameter. Meaning that the thrown exception makes no difference to setting the value or not.
Now for using invoke there is no direct call. I didn't lookup the IL code for this part, but the source is enough to figure out whats going on. First up the Invoke method is called:
public override Object Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
{
object[] arguments = InvokeArgumentsCheck(obj, invokeAttr, binder, parameters, culture);
// [Security Check omitted for readability]
return UnsafeInvokeInternal(obj, parameters, arguments);
}
Notice, that it calls the InvokeArgumentsCheck which returns an array of value called arguments. The method is implemented as follows:
internal Object[] CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
{
// copy the arguments in a different array so we detach from any user changes
Object[] copyOfParameters = new Object[parameters.Length];
// [Code omitted for readability]
for (int i = 0; i < parameters.Length; i++)
{
// [Code omitted for readability]
copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr);
}
return copyOfParameters;
}
The method basically creates an copy of the input parameters you specified (with various type checks in place). As you can see from the comment placed in the method, this is done to prevent any changes by the user from influencing the data will the method is being called.
As last we look into the UnsafeInvokeInternal. The method source looks as follows:
private object UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
{
if (arguments == null || arguments.Length == 0)
return RuntimeMethodHandle.InvokeMethod(obj, null, Signature, false);
else
{
Object retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false);
// copy out. This should be made only if ByRef are present.
for (int index = 0; index < arguments.Length; index++)
parameters[index] = arguments[index];
return retValue;
}
}
As we have arguments, we can focus on the 'else' part. The method is invoked by passing the arguments which is, as we determined before, a copy of the provided parameters. After the call is complete, the argument values are pushed back into the source array 'Parameters'.
In the case of an exception that means that the code gets aborted before it can 'push back' the "Hello" to our output parameter. Most likely (but I've been unable to check) it does change the copied-value in the arguments array, we just cannot access it.
I'll let you decide if this was by design, oversight, or they just thought there shouldn't be a use-case for this anyway.
I am trying to merge the IL of two methods: one method is from an already existing assembly (MyAssembly.dll) and one is in the same project as my dnlib code. I'm trying to read both methods, merge their instructions and then write it to a new assembly. This works, however when I add if statements (probably also other statements) to the new method (so the one from my project) I get an exception when writing the instructions to the new assembly:
Unhandled Exception: dnlib.DotNet.Writer.ModuleWriterException: Found some other method's instruction or a removed instruction. You probably removed an instruction that is the target of a branch instruction or an instruction that's the first/last inst
ruction in an exception handler.
at dnlib.DotNet.DummyLogger.Log(Object sender, LoggerEvent loggerEvent, String format, Object[] args)
at dnlib.DotNet.Writer.ModuleWriterBase.dnlib.DotNet.ILogger.Log(Object sender, LoggerEvent loggerEvent, String format, Object[] args)
at dnlib.DotNet.Writer.MetaData.Error(String message, Object[] args)
at dnlib.DotNet.Writer.MetaData.dnlib.DotNet.Writer.IWriterError.Error(String message)
at dnlib.DotNet.Writer.MethodBodyWriter.ErrorImpl(String message)
at dnlib.DotNet.Writer.MethodBodyWriterBase.Error(String message)
at dnlib.DotNet.Writer.MethodBodyWriterBase.GetOffset(Instruction instr)
at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteShortInlineBrTarget(BinaryWriter writer, Instruction instr)
at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteOperand(BinaryWriter writer, Instruction instr)
at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteInstruction(BinaryWriter writer, Instruction instr)
at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteInstructions(BinaryWriter writer)
at dnlib.DotNet.Writer.MethodBodyWriter.WriteFatHeader()
at dnlib.DotNet.Writer.MethodBodyWriter.Write()
at dnlib.DotNet.Writer.MetaData.WriteMethodBodies()
at dnlib.DotNet.Writer.MetaData.Create()
at dnlib.DotNet.Writer.MetaData.CreateTables()
at dnlib.DotNet.Writer.ModuleWriter.WriteImpl()
at dnlib.DotNet.Writer.ModuleWriterBase.Write(Stream dest)
at dnlib.DotNet.Writer.ModuleWriterBase.Write(String fileName)
at dnlib.DotNet.ModuleDef.Write(String filename, ModuleWriterOptions options)
at dnlib.DotNet.ModuleDef.Write(String filename)
It does not throw an exception when the original code (the code from MyAssembly.dll) contains an if statement.
MyAssembly.dll's code:
string str = GameManager.m_GameVersionString;
if (GameManager.m_Changelist != string.Empty)
{
str = str + " (" + GameManager.m_Changelist + ")";
}
return str + " Release ";
MyAssembly.dll's IL:
IL_0000: ldsfld System.String GameManager::m_GameVersionString
IL_0005: stloc.0
IL_0006: ldsfld System.String GameManager::m_Changelist
IL_000B: ldsfld System.String System.String::Empty
IL_0010: call System.Boolean System.String::op_Inequality(System.String,System.String)
IL_0015: brfalse IL_0030
IL_001A: ldloc.0
IL_001B: ldstr " ("
IL_0020: ldsfld System.String GameManager::m_Changelist
IL_0025: ldstr ")"
IL_002A: call System.String System.String::Concat(System.String,System.String,System.String,System.String)
IL_002F: stloc.0
IL_0030: ldloc.0
IL_0031: ldstr " Release "
IL_0036: call System.String System.String::Concat(System.String,System.String)
IL_003B: stloc.0
IL_003C: ldloc.0
IL_003D: ret
My project's code:
string check = "Hello".Trim();
if (check == "Hello")
{
Console.WriteLine("Hello");
}
My project's IL
IL_0000: ldstr "Hello"
IL_0005: call System.String System.String::Trim()
IL_000A: ldstr "Hello"
IL_000F: call System.Boolean System.String::op_Equality(System.String,System.String)
IL_0014: brfalse.s IL_0020
IL_0016: ldstr "Hello"
IL_001B: call System.Void System.Console::WriteLine(System.String)
IL_0020: ret
Merged IL
IL_0000: ldstr "Hello"
IL_0005: call System.String System.String::Trim()
IL_000A: ldstr "Hello"
IL_000F: call System.Boolean System.String::op_Equality(System.String,System.String)
IL_0014: brfalse.s IL_0020
IL_0016: ldstr "Hello"
IL_001B: call System.Void System.Console::WriteLine(System.String)
IL_0020: ldsfld System.String GameManager::m_GameVersionString
IL_0025: stloc.0
IL_0026: ldsfld System.String GameManager::m_Changelist
IL_002B: ldsfld System.String System.String::Empty
IL_0030: call System.Boolean System.String::op_Inequality(System.String,System.String)
IL_0035: brfalse.s IL_004D
IL_0037: ldloc.0
IL_0038: ldstr " ("
IL_003D: ldsfld System.String GameManager::m_Changelist
IL_0042: ldstr ")"
IL_0047: call System.String System.String::Concat(System.String,System.String,System.String,System.String)
IL_004C: stloc.0
IL_004D: ldloc.0
IL_004E: ldstr " Release "
IL_0053: call System.String System.String::Concat(System.String,System.String)
IL_0058: stloc.0
IL_0059: ldloc.0
IL_005A: ret
The merged IL looks identical to the IL I generated with dnSpy: https://owo.whats-th.is/a0783e.png
However, an error is thrown when writing the merged IL to a file. My code:
//Patch method
...
var patchModule = ModuleDefMD.Load(patchClass.Module);
var patchMethod = FindMethod(patchModule, patchClass, method.Name);
var assemblyModule = ModuleDefMD.Load(assemblyPath);
var assemblyMethod = FindMethod(assemblyModule, attribute.Type, attribute.MethodName, attribute.Parameters);
assemblyMethod.Body = MergeMethods(patchMethod, assemblyMethod, attribute.CodeMode, attribute.CustomPos);
assemblyModule.Write(finalPath); //Error is thrown here
...
private static CilBody MergeMethods(MethodDef original, MethodDef target, AmityPatch.Mode mode, int mergeLoc = 0)
{
original.FreeMethodBody();
target.FreeMethodBody();
var originalBody = original.Body;
var targetBody = target.Body;
var targetModule = target.Module;
var originalInstructions = originalBody.Instructions;
var targetInstructions = targetBody.Instructions;
Console.WriteLine("=original method=");
Console.WriteLine();
foreach (var originalInstruction in originalInstructions)
{
Console.WriteLine(originalInstruction);
}
Console.WriteLine();
Console.WriteLine("=target method=");
Console.WriteLine();
foreach (var targetInstruction in targetInstructions)
{
Console.WriteLine(targetInstruction);
}
RemoveReturn(ref originalInstructions, true);
if (mode == AmityPatch.Mode.Postfix) mergeLoc = targetInstructions.Count - 1;
var localOffset = targetBody.Variables.Count;
for (var i = 0; i < originalBody.Variables.Count; i++)
{
targetBody.Variables.Add(
new Local(originalBody.Variables[i].Type, originalBody.Variables[i].Name));
}
for (var i = originalInstructions.Count - 1; i >= 0; i--)
{
var o = originalInstructions[i];
var c = new Instruction(o.OpCode, o.Operand);
switch (o.Operand)
{
case IType type:
c.Operand = targetModule.Import(type);
break;
case IMethod method:
c.Operand = targetModule.Import(method);
break;
case IField field:
c.Operand = targetModule.Import(field);
break;
}
if (IsStloc(o.OpCode))
{
c.OpCode = OpCodes.Stloc;
c.Operand = targetBody.Variables[StlocIndex(o) + localOffset];
}
else if (IsLdloc(o.OpCode))
{
c.OpCode = OpCodes.Ldloc;
c.Operand = targetBody.Variables[LdlocIndex(o) + localOffset];
}
else if (IsLdloca(o.OpCode))
{
c.OpCode = OpCodes.Ldloca;
c.Operand = targetBody.Variables[LdlocIndex(o) + localOffset];
}
targetInstructions.Insert(mergeLoc, c);
}
targetBody.OptimizeMacros();
targetBody.OptimizeBranches();
Console.WriteLine();
Console.WriteLine("=merged method=");
Console.WriteLine();
foreach (var instruction in targetBody.Instructions)
{
Console.WriteLine(instruction);
}
Console.WriteLine(targetBody.Variables.Count);
return targetBody;
}
I don't know what causes this issue. All labels in the IL seem to be correct. When removing the if statement dnlib writes the IL to the assembly just fine.
If the method to be merged in is from a different assembly, tokens may need to be created/converted.
I am trying to figure out why one of these delegates is working differently under the hood. I have basically these kind of delegates:
private delegate void _SetSomething(bool value);
private delegate Something _Find(string value);
private delegate T _AddSomething<T>();
For each of these I have to know the memory address where they are pointing out, so I used GetFunctionPointer and I got three addresses, nothing special right now except for the fact I have to patch these addresses in memory, so I checked them in disassembly view and I saw for the first two methods the expected behaviours but not for the last:
var obj = new SomethingObj();
obj.SetSomething(true); // E8 XX XX XX XX calling exactly the first address got from the delegate
firstDel.DynamicInvoke(true); // starts calling from the fp value
var ret1 = obj.Find("something"); // E8 XX XX XX XX calling exactly the second address from the delegate
var ret2 = secondDel.DynamicInvoke("something"); // starts calling from the fp value
var ret3 = obj.AddSomething<SomeStuff>(); // E8 XX XX XX XX !!! Here the address does not match with the function pointer got from delegate
var ret4 = thirdDel.DynamicInvoke(null); // starts calling from the fp value...
I am running out of the options, I don't completly understand what is happening with the last delegate except for the fact that it is a little bit more complex of the others.
I don't really understand why is not the same address called by the instance method like the others two, anyone could help me out?
using System;
using System.Linq;
namespace Test
{
internal class Program
{
private static void Main(string[] args)
{
var obj = new SomethingObj();
var firstDel = (_SetSomething) Delegate.CreateDelegate(typeof (_SetSomething), obj, "SetSomething");
var targetFind = typeof (SomethingObj).GetMethod("Find");
var secondDel = (_Find) Delegate.CreateDelegate(typeof (_Find), obj, targetFind, true);
var targetAdd = typeof (SomethingObj).GetMethods()
.First(m => m.Name == "AddSomething" && m.GetParameters().Length == 0)
.MakeGenericMethod(typeof (SomeStuff));
var thirdDel =
(_AddSomething<SomeStuff>) Delegate.CreateDelegate(typeof (_AddSomething<SomeStuff>), obj, targetAdd, true);
var firstAddress = firstDel.Method.MethodHandle.GetFunctionPointer();
var secondAddress = secondDel.Method.MethodHandle.GetFunctionPointer();
var thirdAddress = thirdDel.Method.MethodHandle.GetFunctionPointer();
obj.SetSomething(true); // E8 XX XX XX XX calling exactly the first address got from the delegate
firstDel.DynamicInvoke(true); // starts calling from the fp value
var ret1 = obj.Find("something"); // E8 XX XX XX XX calling exactly the second address from the delegate
var ret2 = secondDel.DynamicInvoke("something"); // starts calling from the fp value
var ret3 = obj.AddSomething<SomeStuff>(); // E8 XX XX XX XX !!! Here the address does not match with the function pointer got from delegate
var ret4 = thirdDel.DynamicInvoke(null); // starts calling from the fp value...
}
public class SomethingObj
{
public void SetSomething(bool value)
{
}
public SomeStuff AddSomething(Type type)
{
return new SomeStuff();
}
public T AddSomething<T>() where T : SomeStuff
{
return new SomeStuff() as T;
}
public SomeStuff AddSomething(string className)
{
return new SomeStuff();
}
public SomeStuff Find(string value)
{
return new SomeStuff();
}
}
public class SomeStuff
{
}
public class SomeStuffTest : SomeStuff
{
}
private delegate void _SetSomething(bool value);
private delegate SomeStuff _Find(string value);
private delegate T _AddSomething<T>();
}
}
I do not want to use a jagged array and I have a 2D array and I want to get a complete column or row without looping through it. Does anyone have an idea how it can be done.
double [,] array = new double [3,3] ;
1 2 3
4 5 6
Out: 1 2 3 or 2 5
To get a specific row or column from the multidimensional array you can use some LINQ:
public class CustomArray<T>
{
public T[] GetColumn(T[,] matrix, int columnNumber)
{
return Enumerable.Range(0, matrix.GetLength(0))
.Select(x => matrix[x, columnNumber])
.ToArray();
}
public T[] GetRow(T[,] matrix, int rowNumber)
{
return Enumerable.Range(0, matrix.GetLength(1))
.Select(x => matrix[rowNumber, x])
.ToArray();
}
}
You can optimise it for getting rows by using Buffer.BlockCopy(), but to get a column you'll have to loop. Buffer.BlockCopy() ultimately uses a processor instruction to copy a block of memory, so it is pretty fast.
It's convenient to put the code into an extension method to make it easier to call. Note that Buffer.BlockCopy() can only be used on arrays of primitive types, i.e. int, double, char etc. This does NOT include string.
Here's a compilable example:
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace ConsoleApplication4
{
public static class Program
{
private static void Main()
{
var array = new [,]
{
{0.1, 0.2, 0.3, 0.4, 0.5},
{1.1, 1.2, 1.3, 1.4, 1.5},
{2.1, 2.2, 2.3, 2.4, 2.5},
{3.1, 3.2, 3.3, 3.4, 3.5},
};
var row = array.GetRow(2);
// This prints 2.1, 2.2, 2.3, 2.4, 2.5
Console.WriteLine(string.Join(", ", row.Select(element => element.ToString())));
}
}
public static class ArrayExt
{
public static T[] GetRow<T>(this T[,] array, int row)
{
if (!typeof(T).IsPrimitive)
throw new InvalidOperationException("Not supported for managed types.");
if (array == null)
throw new ArgumentNullException("array");
int cols = array.GetUpperBound(1) + 1;
T[] result = new T[cols];
int size;
if (typeof(T) == typeof(bool))
size = 1;
else if (typeof(T) == typeof(char))
size = 2;
else
size = Marshal.SizeOf<T>();
Buffer.BlockCopy(array, row*cols*size, result, 0, cols*size);
return result;
}
}
}
As of March 2021, you can now use the very cool Span2D class for this!
If you are happy using spans (I highly recommend reading about them, they are awesome), you can use the following code
var span2D = new Span2D<double>(array);
//Gets the first row and returns a span of it
var rowSpan = span2D.GetRowSpan(0);
foreach(var number in rowSpan)
{
//Do something with numbers
}
//Gets the 2nd Column as a RefEnumerable and converts it to an array
var column = span2D.GetColumn(1).ToArray();
Here is how i have done it you can use
GetLength(0)
to get the columns and use
GetLength(1)
to get the rows of the 2 Dimensional array and you loop thru it with the for loop if any one else needs this.
string text = "";
for (int i = 0; i < array.GetLength(0); i++)
{
text += Convert.ToString(array[i, 2]) + "\n";
}
an alternative way you can do it is by using a List instead of an array.
Specifically in your case you'd do something like that:
Initially create an inner class that represents a tuple of the array
Create a List of the inner class
Populate the inner class
Get the row that contains something specific
Get the column that contains something specific
public static void Main(string[] args)
{
// #2 -- Instantiate List of myClass
List<myClass> myList = new List<myClass>();
//
// #3 -- Populate the list
myList.Add(new myClass(1,2,3));
myList.Add(new myClass(3,4,5));
myList.Add(new myClass(5,6,6));
//
// #4 -- Get the line where a == 1
myList.Find(x=>x.a == 1);
//
// #5 -- Get column b
myList.Select(x=>x.b);
}
// #1 -- Create the inner class
public class myClass
{
public int a;
public int b;
public int c;
public myClass(int a, int b, int c)
{
this.a =a;
this.b =b;
this.c =c;
}
}
what is needed is a jagged array (not a multidimensional array)
https://msdn.microsoft.com/en-us/library/2s05feca.aspx
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[5];
jaggedArray[1] = new int[] { 0, 2, 4, 6 };
jaggedArray[2] = new int[] { 11, 22 };
full example with columns:
using System;
using System.Collections.Generic;
namespace Rextester
{
public class Program
{
public static T[] column<T>(T[][] jaggedArray,int wanted_column)
{
T[] columnArray = new T[jaggedArray.Length];
T[] rowArray;
for(int i=0;i<jaggedArray.Length;i++)
{
rowArray=jaggedArray[i];
if(wanted_column<rowArray.Length)
columnArray[i]=rowArray[wanted_column];
}
return columnArray;
}
public static void Main(string[] args)
{
//Your code goes here
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[5];
jaggedArray[1] = new int[] { 0, 2, 4, 6 };
jaggedArray[2] = new int[] { 11, 22 };
Console.WriteLine("Hello, world!");
Console.WriteLine(string.Join(" ",jaggedArray[1]));
Console.WriteLine(string.Join(" ",column(jaggedArray,1)));
}
}
}
similar idea, using extensions:
using System;
using System.Collections.Generic;
namespace Rextester
{
public static class MyExtensions
{
public static string Extend(this Array array)
{
return "Yes, you can extend an array";
}
public static T[] column<T>(this T[,] multidimArray,int wanted_column)
{
int l=multidimArray.GetLength(0);
T[] columnArray = new T[l];
for(int i=0;i<l;i++)
{
columnArray[i]=multidimArray[i,wanted_column];
}
return columnArray;
}
public static T[] row<T>(this T[,] multidimArray,int wanted_row)
{
int l=multidimArray.GetLength(1);
T[] rowArray = new T[l];
for(int i=0;i<l;i++)
{
rowArray[i]=multidimArray[wanted_row,i];
}
return rowArray;
}
}
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, world!");
int [,] multidimArray = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
Console.WriteLine(string.Join(" ",multidimArray.column(0)));
Console.WriteLine(string.Join(" ",multidimArray.row(0)));
}
}
}
My use case differs from the question, but is similar. I needed a 2D array of float[2] arrays, that I was using to represent complex numbers.
float[,,] myarray = new float[100,100,2];
float[] result = myarray[1,1]; <-- fails to compile needs all 3 coordinates
The jagged array Simmon mentioned provided the solution.
float[,][] myarray = new float[100,100][];
...
myarray[x,y] = new float[2]; <-- Initialise all elements of jagged 2D array in loop
...
float[] result = [100,100];
if you know the index of the numbers to output..then you don't need to use a loop to get the output desired...
double[,] array = new double[3,3] {{1,2,3}, {4,5,6}, {7,8,9}};
int firstNum = array[0,1];
int secondNum = array[1,1];
this will get 2, 5