Getting the field a MemberRef metadata token refers to - c#

Fair warning, this may be a tad esoteric and tricky.
Given a MemberRef (more explanation below) extracted from a CIL stream, how do you figure out what field, if any, it points to (and get a FieldInfo for it)?
Here's what I've figured out so far
According to the ECMA 335 standard, a MemberRef is a metadata token that is basically a lookup in a table that could point to either a field metadata token or a method metadata token. Any metadata token beginning 0x0A is a MemberRef.
I hadn't encountered one of these before, but they don't seem that uncommon. I was able to get one generated by using the following anonymous type in a method:
new
{
A = new DateTime(1234, 5, 6, 7, 8, 9, DateTimeKind.Utc),
B = (DateTime?)null
}
When I grab the method body via reflection (get the PropertyInfo, get the GetMethod, get the MethodBody, then finally get the IL) A's get method is:
[2, 123, 79, 0, 0, 10, 42]
Which converts to:
ldarg.0
ldfld 0x0A00004F
ret
If I reflect in and get the backing field (relying on name similarity to choose <A>i__Field, nothing algorithmic) I see that the MetadataToken is 0x04000056.
Note that the tokens generated may vary between compilations.
A token starting 0x04 is a field:
Most of the time (for all non-anonymous objects in my limited testing, in fact) the IL contains a field metadata token. This is easy to turn into a FieldInfo via Module.ResolveField(int), figuring out what to do with a MemberRef is tripping me up.
Cycling through the other ResolveXXX methods on Module, the only one that can do anything with a MemberRef is ResolveSignature. When run on the above MemberRef, it returns an array of [6, 19, 0]. I don't really know what to make of that.
The code I'm working on is unfinished, but public. The error can be seen by running this test, causing an exception to be thrown when a field lookup fails on this line. Note that the test itself is unfinished, it's not expected to succeed yet but it shouldn't die there either.
Anybody have any idea what to make of that signature, or some other way to get to a field's metadata token (and thus its FieldInfo) from the MemberRef?
Here's a LINQPad program script that reproduces the problem. It's pretty big, there's a lot of boilerplate.
void Main()
{
Init();
var obj =
new
{
A = new DateTime(1234, 5, 6, 7, 8, 9, DateTimeKind.Utc),
B = (DateTime?)null
};
var usage = PropertyFieldUsage(obj.GetType());
usage.Dump();
}
private static Dictionary<int, System.Reflection.Emit.OpCode> OneByteOps;
private static Dictionary<int, System.Reflection.Emit.OpCode> TwoByteOps;
public static Dictionary<PropertyInfo, List<FieldInfo>> PropertyFieldUsage(Type t)
{
var ret = new Dictionary<PropertyInfo, List<FieldInfo>>();
var props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic).Where(p => p.GetMethod != null);
var module = t.Module;
foreach (var prop in props)
{
var getMtd = prop.GetMethod;
var mtdBody = getMtd.GetMethodBody();
var il = mtdBody.GetILAsByteArray();
var fieldHandles = _GetFieldHandles(il);
var fieldInfos =
fieldHandles
.Select(
f => module.ResolveField(f)
).ToList();
ret[prop] = fieldInfos;
}
return ret;
}
// Define other methods and classes here
private static List<int> _GetFieldHandles(byte[] cil)
{
var ret = new List<int>();
int i = 0;
while (i < cil.Length)
{
int? fieldHandle;
System.Reflection.Emit.OpCode ignored;
var startsAt = i;
i += _ReadOp(cil, i, out fieldHandle, out ignored);
if (fieldHandle.HasValue)
{
ret.Add(fieldHandle.Value);
}
}
return ret;
}
private static int _ReadOp(byte[] cil, int ix, out int? fieldHandle, out System.Reflection.Emit.OpCode opcode)
{
const byte ContinueOpcode = 0xFE;
int advance = 0;
byte first = cil[ix];
if (first == ContinueOpcode)
{
var next = cil[ix + 1];
opcode = TwoByteOps[next];
advance += 2;
}
else
{
opcode = OneByteOps[first];
advance++;
}
fieldHandle = _ReadFieldOperands(opcode, cil, ix, ix + advance, ref advance);
return advance;
}
private static int? _ReadFieldOperands(System.Reflection.Emit.OpCode op, byte[] cil, int instrStart, int operandStart, ref int advance)
{
Func<int, int> readInt = (at) => cil[at] | (cil[at + 1] << 8) | (cil[at + 2] << 16) | (cil[at + 3] << 24);
switch (op.OperandType)
{
case System.Reflection.Emit.OperandType.InlineBrTarget:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineSwitch:
advance += 4;
var len = readInt(operandStart);
var offset1 = instrStart + len * 4;
for (var i = 0; i < len; i++)
{
advance += 4;
}
return null;
case System.Reflection.Emit.OperandType.ShortInlineBrTarget:
advance += 1;
return null;
case System.Reflection.Emit.OperandType.InlineField:
advance += 4;
var field = readInt(operandStart);
return field;
case System.Reflection.Emit.OperandType.InlineTok:
case System.Reflection.Emit.OperandType.InlineType:
case System.Reflection.Emit.OperandType.InlineMethod:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineI:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineI8:
advance += 8;
return null;
case System.Reflection.Emit.OperandType.InlineNone:
return null;
case System.Reflection.Emit.OperandType.InlineR:
advance += 8;
return null;
case System.Reflection.Emit.OperandType.InlineSig:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineString:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.InlineVar:
advance += 2;
return null;
case System.Reflection.Emit.OperandType.ShortInlineI:
advance += 1;
return null;
case System.Reflection.Emit.OperandType.ShortInlineR:
advance += 4;
return null;
case System.Reflection.Emit.OperandType.ShortInlineVar:
advance += 1;
return null;
default: throw new Exception("Unexpected operand type [" + op.OperandType + "]");
}
}
static void Init()
{
var oneByte = new List<System.Reflection.Emit.OpCode>();
var twoByte = new List<System.Reflection.Emit.OpCode>();
foreach (var field in typeof(System.Reflection.Emit.OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static))
{
var op = (System.Reflection.Emit.OpCode)field.GetValue(null);
if (op.Size == 1)
{
oneByte.Add(op);
continue;
}
if (op.Size == 2)
{
twoByte.Add(op);
continue;
}
throw new Exception("Unexpected op size for " + op);
}
OneByteOps = oneByte.ToDictionary(d => (int)d.Value, d => d);
TwoByteOps = twoByte.ToDictionary(d => (int)(d.Value & 0xFF), d => d);
}

The trick here is that it is a generic type (the second parameter to ResolveField), and we know that the getter is not a generic method (the final parameter to ResolveField), so you need to use ResolveField like so:
var obj = new
{
A = new DateTime(1234, 5, 6, 7, 8, 9, DateTimeKind.Utc),
B = (DateTime?)null
};
Parse(obj, "A");
Parse(obj, "B");
static void Parse(object obj, string property)
{
var blob = obj.GetType().GetProperty(property).GetGetMethod()
.GetMethodBody().GetILAsByteArray();
// hard-code that we know the token is at offset 2
int token = BitConverter.ToInt32(blob, 2);
var field = obj.GetType().Module.ResolveField(token,
obj.GetType().GetGenericArguments(), null);
Console.WriteLine(field.Name);
Console.WriteLine(field.MetadataToken);
}
In the more general case, where you don't know as much about the type (i.e. that it might be a non-generic type) and the method (although strictly speaking, property accessors are never generic in their own right, but this shows broad usage):
static MemberInfo ResolveMember(this MethodInfo method, int metadataToken)
{
Type type = method.DeclaringType;
Type[] typeArgs = null, methodArgs = null;
if (type.IsGenericType || type.IsGenericTypeDefinition)
typeArgs = type.GetGenericArguments();
if (method.IsGenericMethod || method.IsGenericMethodDefinition)
methodArgs = method.GetGenericArguments();
return type.Module.ResolveMember(metadataToken, typeArgs, methodArgs);
}

Related

Add range dynamically to list

I have a List<byte> that stores the value of a variable byte by byte. I am trying to build up this variable by respect to its original data type.
Example of the result:
List<byte> varBytes = new List<byte>();
varBytes.Add(0x12);
varBytes.Add(0x34);
varBytes.Add(0x56);
varBytes.Add(0x78);
//After the conversion of UInt32:
varReady = 0x78563412;
Here is a snippet of my class that returns the value of the variable.
public static object GetTypedString(List<byte> varBytes, string varType)
{
object varReady;
switch (varType)
{
case "uint16":
UInt16 varReady = BitConverter.ToUInt16(varBytes.ToArray<byte>(), 0);
break;
case "uint32":
UInt32 varReady = BitConverter.ToUInt32(varBytes.ToArray<byte>(), 0);
break;
//repeat case for each data type
}
return varReady ;
}
The problem comes up if my variable is only 2 bytes long and if I want to show that variable as UInt32. The BitConverter.ToUInt32 will throw this exception:
Destination array is not long enough to copy all the items in the collection.
Because the varBytes list only has 2 bytes but BitConverter.ToUInt32 is trying to read 4 bytes. My solution was to add dummy bytes to the end of the list in this case:
.
.
.
case "uint32":
int difference = sizeof(UInt32) - varSize; //we know the variable size already
if(difference > 0)
{
varToDisp.value.AddRange(new byte[difference]);
}
UInt32 varReady = BitConverter.ToUInt32(varBytes.ToArray<byte>(), 0);
break;
.
.
.
This works but didn't seem a good way to me since it will edit the original List and consumes some time. Is there any easier way to achieve this?
You can create the array (not list) with required Length with a help of Linq Concat; I suggest routine redesign as well.
Code:
// Let's implement a generic method: we want, say, uint not object from given list
public static T GetTypedString<T>(List<byte> varBytes) where T: struct {
if (null == varBytes)
throw new ArgumentNullException(nameof(varBytes));
// sizeof alternative
// char is Ascii by default when marshalling; that's why Marshal.SizeOf returns 1
int size = typeof(T) == typeof(char)
? sizeof(char)
: System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
// if data is too short we should pad it; either from left or from right:
// {0, ..., 0} + data or data + {0, ..., 0}
// to choose the way, let's have a look at endiness
byte[] data = (size >= varBytes.Count)
? BitConverter.IsLittleEndian
? varBytes.Concat(new byte[size - varBytes.Count]).ToArray()
: new byte[size - varBytes.Count].Concat(varBytes).ToArray()
: varBytes.ToArray();
// A bit of reflection: let's find out suitable Converter method
var mi = typeof(BitConverter).GetMethod($"To{typeof(T).Name}");
if (null == mi)
throw new InvalidOperationException($"Type {typeof(T).Name} can't be converted");
else
return (T)(mi.Invoke(null, new object[] { data, 0 })); // or data.Length - size
}
Then you can use it as follow:
List<byte> varBytes = new List<byte>();
varBytes.Add(0x12);
varBytes.Add(0x34);
varBytes.Add(0x56);
varBytes.Add(0x78);
int result1 = GetTypedString<int>(varBytes);
long result2 = GetTypedString<long>(varBytes);
Console.WriteLine(result1.ToString("x"));
Console.WriteLine(result2.ToString("x"));
// How fast it is (Linq and Reflection?)
var sw = new System.Diagnostics.Stopwatch();
int n = 10000000;
sw.Start();
for (int i = 0; i < n; ++i) {
// The worst case:
// 1. We should expand the array
// 2. The output is the longest one
long result = GetTypedString<long>(varBytes);
//Trick: Do not let the compiler optimize the loop
if (result < 0)
break;
}
sw.Stop();
Console.WriteLine($"Microseconds per operation: {(sw.Elapsed.TotalSeconds/n*1000000)}");
Outcome:
78563412
78563412
Microseconds per operation: 0.84716933
Edit: If you insist on type name (string varType) instead of generic parameter <T> first of all let's extract a model (type name - type correspondense):
private static Dictionary<string, Type> s_Types =
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase) {
{ "uint16", typeof(UInt16)},
{ "ushort", typeof(UInt16)}, // <- you can add synonyms if you want
{ "int", typeof(Int32)},
{ "int32", typeof(Int32)},
{ "long", typeof(Int64)},
{ "int64", typeof(Int64)},
//TODO: add all the other names and correspondent types
};
Then you can implement it as
public static object GetTypedString(List<byte> varBytes, string varType) {
if (null == varBytes)
throw new ArgumentNullException(nameof(varBytes));
else if (null == varType)
throw new ArgumentNullException(nameof(varType));
Type type = null;
if (!s_Types.TryGetValue(varType, out type))
throw new ArgumentException(
$"Type name {varType} is not a valid type name.",
nameof(varBytes));
// sizeof alternative
// char is Ascii by default when marshalling; that's why Marshal.SizeOf returns 1
int size = typeof(T) == typeof(char)
? sizeof(char)
: System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
byte[] data = (size >= varBytes.Count)
? BitConverter.IsLittleEndian
? varBytes.Concat(new byte[size - varBytes.Count]).ToArray()
: new byte[size - varBytes.Count].Concat(varBytes).ToArray()
: varBytes.ToArray();
var mi = typeof(BitConverter).GetMethod($"To{type.Name}");
if (null == mi)
throw new InvalidOperationException(
$"Type {type.Name} (name: {varType}) can't be converted");
else
return mi.Invoke(null, new object[] { data, 0 }); // data.Length - size
}
Demo:
string result1 = (GetTypedString(varBytes, "Int64") as IFormattable).ToString("x8", null);
Rather than using .ToArray you could preallocate your array to the correct size and use .CopyTo.
Example:
var byteArray = new byte[sizeof(UInt32)];
varBytes.CopyTo(byteArray);
UInt32 varReady = BitConverter.ToUInt32(byteArray, 0);
You can check for the length of the array and convert it to smaller types then cast the required one
case "uint32":
{
if (varBytes.Count == 1)
{
varReady = (UInt32)varBytes[0];
}
else if (varBytes.Count >= 2 && varBytes.Count < 4)
{
varReady = (UInt32)BitConverter.ToUInt16(varBytes.ToArray<byte>(), 0);
}
else
{
varReady = BitConverter.ToUInt32(varBytes.ToArray<byte>(), 0);
}
break;
}

C# Passing struct as parameter

I have the following code in C#:
public class ELL
{
public struct RVector
{
private int ndim;
private double[] vector;
public RVector(double[] vector) => (ndim, this.vector) = (vector.Length, vector);
public double this[int i] { get => vector[i]; set => vector[i] = value; }
public override string ToString()
{
string str = "(";
for (int i = 0; i < ndim - 1; i++)
str += vector[i].ToString() + ", ";
str += vector[ndim - 1].ToString() + ")";
return str;
}
}
private static void SwapVectorEntries(RVector b, int m, int n)
{
double temp = b[m];
b[m] = b[n];
b[n] = temp;
}
public static void M(string[] args)
{
var a = new double[4] { 1, 2, 3, 4 };
var b = new RVector(a);
Console.WriteLine(b);
SwapVectorEntries(b, 1, 2); // Why after this command, b will be changed?
Console.WriteLine(b);
}
}
In this program, i creates a struct RVector. After that, i use a method SwapVectorEntries which have a struct parameter. Because, Struct is a value type, so i think the method SwapVectorEntries will not change the struct parameter. But, in the program, after the command SwapVectorEntries(b, 1, 2);, b has changed. Please explain me about this. Thank you !
Problem is in this.You have an array wich is reference type.When you create your
double[] a = new double[4] { 1, 2, 3, 4 };
RVector b = new RVector(a);
you have two references to that array.After when you pass your object into the method,
SwapVectorEntries(b, 1, 2);
your object is copied,BUT your new object have the same reference to that array.Here your have only one array and many references to it.
B itself is not passed as a reference, but the copy of b has a Reference to the same double[].

c# and LINQ where property-name of collection member is passed via function

As always, help/comments thoughts are always appreciated, and appologies for the naivety of my programming.
I am trying to create a broadly applicable function which can be used in future research which involves block randomization. Each member of patientDataCollection will have a boolean property named something like interventionArm, givenDrugX or something similar. The function aims to (pseudo-)randomly assign to an arm of a study, depending on block size - that is to say if block size is 8, 4 will be assigned to treatment and 4 to control (no treatment).
The code so far:
public static bool nextAllocation<T>(int blockSize, IEnumerable<T> patientDataCollection, string allocationPropertyName)
{
int remainingAllocations = blockSize - patientDataCollection.Count();
if (remainingAllocations <= 0) throw new Exception("All alocations within block accounted for");
var p = typeof(T).GetProperty(allocationPropertyName);
int remainingInterventions = blockSize/2 - patientDataCollection.Count(c => c.p);
double Pintervention = (double)remainingInterventions / (double)remainingAllocations;
double rdm = new Random().NextDouble();
return (rdm <= Pintervention);
}
this is of course flawed logic because the variable p does not relate to c.p referenced in the linq statement patientDataCollection.Count(c => c.p). obviously this statement is simply to count all elements which have a true value.
ASP is 4.0. Can anyone see how to achieve this
You can pass to your method a Func<T, bool> which will be used for counting.
public static bool nextAllocation<T>(int blockSize, IEnumerable<T> patientDataCollection, Func<T,bool> predicate)
{
int remainingAllocations = blockSize - patientDataCollection.Count();
if (remainingAllocations == 0) throw new Exception("All alocations within block accounted for");
int remainingInterventions = blockSize/2 - patientDataCollection.Count(predicate);
double Pintervention = remainingInterventions / remainingAllocations;
double rdm = new Random().NextDouble();
return (rdm <= Pintervention);
}
An example of usage would be something like this:
var result = nextAllocation(10, collection, c=>c.interventionArm);
You can use reflection to get the value of the property:
int remainingInterventions = blockSize/2 -
patientDataCollection.Count(c => (bool)p.GetValue(c,null));
Add error checking as well.
Try using Expressions
public static class ExpressionCreator
{
public static Func<T, TProperty> CreatePropertyAccessExpression<T, TProperty>(string propertyName)
{
var tType = typeof (T);
var property = tType.GetProperty(propertyName);
var parameterExpression = Expression.Parameter(tType);
var memberAccessExpression = Expression.MakeMemberAccess(parameterExpression, property);
var lambda = Expression.Lambda<Func<T, TProperty>>(memberAccessExpression, parameterExpression);
return lambda.Compile();
}
}
Example usage:
public class A
{
public bool Thing1 { get; set; }
public bool Thing2 { get; set; }
}
static void Main(string[] args)
{
var #as = new A[10];
for(var i = 0; i < #as.Length; i+=2)
{
#as[i] = new A {Thing1 = true};
#as[i + 1] = new A {Thing2 = i%4 == 0};
}
var thing1Expression = ExpressionCreator.CreatePropertyAccessExpression<A, bool>("Thing1");
var thing2Expression = ExpressionCreator.CreatePropertyAccessExpression<A, bool>("Thing2");
Console.WriteLine(#as.Count(thing1Expression));
Console.WriteLine(#as.Count(thing2Expression));
Console.ReadLine();
}

How to access a member of a list inside a delegate

Well I think the title of this question is far to be clear but I hope the text below can clarity it.
The code below is a concept only but illustrates what I'm trying to do. How can I access a member of a List (inside the delegate) returned by a method without assigning it to a local List variable? Is that possible?
static void ListTest()
{
int count = 0;
List<int> even = Load().FindAll(delegate(int x)
{
count = Count; //<------- How can I get the Count from the List returned by the Load method?
return (x % 2) == 0;
});
System.Console.WriteLine("Count = {0} and there are {1} even numbers", count, even.Count);
}
static List<int> Load()
{
List<int> array = new List<int>();
int[] vet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
array.AddRange(vet);
return array;
}
Here's the original code - just to illustrate why I didn't want to create a new List, because it will only be a temporary list without any other use.
private List<MailItem> Find<T_Item, T_Adaptor>(T_Adaptor adaptor, MailItemId MailId)
{
List<T_Item> Items = ((List<T_Item>)(typeof(T_Adaptor).InvokeMember(
"Load",
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
null, adaptor,
new Object[] { null, MailId, null }))).FindAll(
delegate(T_Item itm)
{
MailItem mi = itm as MailItem;
if (mi == null) return false;
return (mi.StateInd.Code == StateInd.ASSIGNED);
});
List<MailItem> mailItems = new List<MailItem>();
foreach (T_Item itm in Items)
mailItems.Add(itm as MailItem);
return mailItems;
}
Thanks
var load = Load();
List<int> even = load.FindAll(delegate(int x)
{
count = load.Count;
return (x % 2) == 0;
});
Edit:
One more idea about how to achieve the result you want using extension methods as syntactical sugar:
static public class ListExt
{
static public List<T> StoreSize<T>(this List<T> self, out int size)
{
size = (self != null) ? self.Count : -1;
return self;
}
}
...
List<int> even = Load().StoreSize(out count).FindAll(x => (x % 2) == 0);
Unfortunately the list reference isn't available to your code since it only lives on the stack as part of temporary expression results used during the call chain.
As such, your only option is to explicitly store it away into a variable before using it.
In this example, I would do this:
var list = Load();
List<int> even = list.FindAll(delegate(int x)
{
count = list.Count;
return (x % 2) == 0;
});
Of course, now you can simply do:
var list = Load();
int count = list.Count;
List<int> even = list.FindAll(delegate(int x)
{
return (x % 2) == 0;
});
And also rewrite to a lambda:
var list = Load();
int count = list.Count;
List<int> even = list.FindAll(x => (x % 2) == 0);
List<int> nums = Load();
List<int> even = nums.FindAll(delegate(int x)
{
count = nums.Count;
return (x % 2) == 0;
});
Otherwise, the object you need is not in the scope of the delagate.

How do I clone a range of array elements to a new array?

I have an array X of 10 elements. I would like to create a new array containing all the elements from X that begin at index 3 and ends in index 7. Sure I can easily write a loop that will do it for me but I would like to keep my code as clean as possible. Is there a method in C# that can do it for me?
Something like (pseudo code):
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)
Array.Copy doesn't fit my needs. I need the items in the new array to be clones. Array.copy is just a C-Style memcpy equivalent, it's not what I'm looking for.
You could add it as an extension method:
public static T[] SubArray<T>(this T[] data, int index, int length)
{
T[] result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
static void Main()
{
int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] sub = data.SubArray(3, 4); // contains {3,4,5,6}
}
Update re cloning (which wasn't obvious in the original question). If you really want a deep clone; something like:
public static T[] SubArrayDeepClone<T>(this T[] data, int index, int length)
{
T[] arrCopy = new T[length];
Array.Copy(data, index, arrCopy, 0, length);
using (MemoryStream ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, arrCopy);
ms.Position = 0;
return (T[])bf.Deserialize(ms);
}
}
This does require the objects to be serializable ([Serializable] or ISerializable), though. You could easily substitute for any other serializer as appropriate - XmlSerializer, DataContractSerializer, protobuf-net, etc.
Note that deep clone is tricky without serialization; in particular, ICloneable is hard to trust in most cases.
You can use Array.Copy(...) to copy into the new array after you've created it, but I don't think there's a method which creates the new array and copies a range of elements.
If you're using .NET 3.5 you could use LINQ:
var newArray = array.Skip(3).Take(5).ToArray();
but that will be somewhat less efficient.
See this answer to a similar question for options for more specific situations.
Have you considered using ArraySegment?
http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx
I see you want to do Cloning, not just copying references.
In this case you can use .Select to project array members to their clones.
For example, if your elements implemented IClonable you could do something like this:
var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();
Note: This solution requires .NET Framework 3.5.
The following code does it in one line:
// Source array
string[] Source = new string[] { "A", "B", "C", "D" };
// Extracting a slice into another array
string[] Slice = new List<string>(Source).GetRange(2, 2).ToArray();
In C# 8, they've introduced a new Range and Index type, which can be used like this:
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Index i1 = 3; // number 3 from beginning
Index i2 = ^4; // number 4 from end
var slice = a[i1..i2]; // { 3, 4, 5 }
References:
https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#ranges-and-indices
https://devblogs.microsoft.com/dotnet/building-c-8-0/
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };
arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
Building on Marc's answer but adding the desired cloning behaviour
public static T[] CloneSubArray<T>(this T[] data, int index, int length)
where T : ICloneable
{
T[] result = new T[length];
for (int i = 0; i < length; i++)
{
var original = data[index + i];
if (original != null)
result[i] = (T)original.Clone();
return result;
}
And if implementing ICloneable is too much like hard work a reflective one using Håvard Stranden’s Copyable library to do the heavy lifting required.
using OX.Copyable;
public static T[] DeepCopySubArray<T>(
this T[] data, int index, int length)
{
T[] result = new T[length];
for (int i = 0; i < length; i++)
{
var original = data[index + i];
if (original != null)
result[i] = (T)original.Copy();
return result;
}
Note that the OX.Copyable implementation works with any of:
For the automated copy to work, though, one of the following statements must hold for instance:
Its type must have a parameterless constructor, or
It must be a Copyable, or
It must have an IInstanceProvider registered for its type.
So this should cover almost any situation you have. If you are cloning objects where the sub graph contains things like db connections or file/stream handles you obviously have issues but that it true for any generalized deep copy.
If you want to use some other deep copy approach instead this article lists several others so I would suggest not trying to write your own.
You can do this fairly easially;
object[] foo = new object[10];
object[] bar = new object[7];
Array.Copy(foo, 3, bar, 0, 7);
I think that the code you are looking for is:
Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)
As an alternative to copying the data you can make a wrapper that gives you access to a part of the original array as if it was a copy of the part of the array. The advantage is that you don't get another copy of the data in memory, and the drawback is a slight overhead when accessing the data.
public class SubArray<T> : IEnumerable<T> {
private T[] _original;
private int _start;
public SubArray(T[] original, int start, int len) {
_original = original;
_start = start;
Length = len;
}
public T this[int index] {
get {
if (index < 0 || index >= Length) throw new IndexOutOfRangeException();
return _original[_start + index];
}
}
public int Length { get; private set; }
public IEnumerator<T> GetEnumerator() {
for (int i = 0; i < Length; i++) {
yield return _original[_start + i];
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
Usage:
int[] original = { 1, 2, 3, 4, 5 };
SubArray<int> copy = new SubArray<int>(original, 2, 2);
Console.WriteLine(copy.Length); // shows: 2
Console.WriteLine(copy[0]); // shows: 3
foreach (int i in copy) Console.WriteLine(i); // shows 3 and 4
In C# 8.0, you can now do many fancier works including reverse indices and ranges like in Python, such as:
int[] list = {1, 2, 3, 4, 5, 6};
var list2 = list[2..5].Clone() as int[]; // 3, 4, 5
var list3 = list[..5].Clone() as int[]; // 1, 2, 3, 4, 5
var list4 = list[^4..^0].Clone() as int[]; // reverse index
Array.ConstrainedCopy will work.
public static void ConstrainedCopy (
Array sourceArray,
int sourceIndex,
Array destinationArray,
int destinationIndex,
int length
)
It does not meet your cloning requirement, but it seems simpler than many answers to do:
Array NewArray = new ArraySegment(oldArray,BeginIndex , int Count).ToArray();
There's no single method that will do what you want. You will need to make a clone method available for the class in your array. Then, if LINQ is an option:
Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray();
class Foo
{
public Foo Clone()
{
return (Foo)MemberwiseClone();
}
}
How about useing Array.ConstrainedCopy:
int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
Array.ConstrainedCopy(ArrayOne, 3, ArrayTwo, 0, 7-3);
Below is my original post. It will not work
You could use Array.CopyTo:
int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
ArrayOne.CopyTo(ArrayTwo,3); //starts copy at index=3 until it reaches end of
//either array
How about this:
public T[] CloneCopy(T[] array, int startIndex, int endIndex) where T : ICloneable
{
T[] retArray = new T[endIndex - startIndex];
for (int i = startIndex; i < endIndex; i++)
{
array[i - startIndex] = array[i].Clone();
}
return retArray;
}
You then need to implement the ICloneable interface on all of the classes you need to use this on but that should do it.
I'm not sure how deep it really is, but:
MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()
It's a bit of overhead, but it might cut out an unnecessary method.
As far as cloning goes, I don't think serialization calls your constructors. This may break class invariants if you're doing interesting things in the ctor's.
It seems the safer bet is virtual clone methods calling copy constructors.
protected MyDerivedClass(MyDerivedClass myClass)
{
...
}
public override MyBaseClass Clone()
{
return new MyDerivedClass(this);
}
Cloning elements in an array is not something that can be done in a universal way. Do you want deep cloning or a simple copy of all members?
Let's go for the "best effort" approach: cloning objects using the ICloneable interface or binary serialization:
public static class ArrayExtensions
{
public static T[] SubArray<T>(this T[] array, int index, int length)
{
T[] result = new T[length];
for (int i=index;i<length+index && i<array.Length;i++)
{
if (array[i] is ICloneable)
result[i-index] = (T) ((ICloneable)array[i]).Clone();
else
result[i-index] = (T) CloneObject(array[i]);
}
return result;
}
private static object CloneObject(object obj)
{
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, obj);
stream.Seek(0,SeekOrigin.Begin);
return formatter.Deserialize(stream);
}
}
}
This is not a perfect solution, because there simply is none that will work for any type of object.
You can take class made by Microsoft:
internal class Set<TElement>
{
private int[] _buckets;
private Slot[] _slots;
private int _count;
private int _freeList;
private readonly IEqualityComparer<TElement> _comparer;
public Set()
: this(null)
{
}
public Set(IEqualityComparer<TElement> comparer)
{
if (comparer == null)
comparer = EqualityComparer<TElement>.Default;
_comparer = comparer;
_buckets = new int[7];
_slots = new Slot[7];
_freeList = -1;
}
public bool Add(TElement value)
{
return !Find(value, true);
}
public bool Contains(TElement value)
{
return Find(value, false);
}
public bool Remove(TElement value)
{
var hashCode = InternalGetHashCode(value);
var index1 = hashCode % _buckets.Length;
var index2 = -1;
for (var index3 = _buckets[index1] - 1; index3 >= 0; index3 = _slots[index3].Next)
{
if (_slots[index3].HashCode == hashCode && _comparer.Equals(_slots[index3].Value, value))
{
if (index2 < 0)
_buckets[index1] = _slots[index3].Next + 1;
else
_slots[index2].Next = _slots[index3].Next;
_slots[index3].HashCode = -1;
_slots[index3].Value = default(TElement);
_slots[index3].Next = _freeList;
_freeList = index3;
return true;
}
index2 = index3;
}
return false;
}
private bool Find(TElement value, bool add)
{
var hashCode = InternalGetHashCode(value);
for (var index = _buckets[hashCode % _buckets.Length] - 1; index >= 0; index = _slots[index].Next)
{
if (_slots[index].HashCode == hashCode && _comparer.Equals(_slots[index].Value, value))
return true;
}
if (add)
{
int index1;
if (_freeList >= 0)
{
index1 = _freeList;
_freeList = _slots[index1].Next;
}
else
{
if (_count == _slots.Length)
Resize();
index1 = _count;
++_count;
}
int index2 = hashCode % _buckets.Length;
_slots[index1].HashCode = hashCode;
_slots[index1].Value = value;
_slots[index1].Next = _buckets[index2] - 1;
_buckets[index2] = index1 + 1;
}
return false;
}
private void Resize()
{
var length = checked(_count * 2 + 1);
var numArray = new int[length];
var slotArray = new Slot[length];
Array.Copy(_slots, 0, slotArray, 0, _count);
for (var index1 = 0; index1 < _count; ++index1)
{
int index2 = slotArray[index1].HashCode % length;
slotArray[index1].Next = numArray[index2] - 1;
numArray[index2] = index1 + 1;
}
_buckets = numArray;
_slots = slotArray;
}
internal int InternalGetHashCode(TElement value)
{
if (value != null)
return _comparer.GetHashCode(value) & int.MaxValue;
return 0;
}
internal struct Slot
{
internal int HashCode;
internal TElement Value;
internal int Next;
}
}
and then
public static T[] GetSub<T>(this T[] first, T[] second)
{
var items = IntersectIteratorWithIndex(first, second);
if (!items.Any()) return new T[] { };
var index = items.First().Item2;
var length = first.Count() - index;
var subArray = new T[length];
Array.Copy(first, index, subArray, 0, length);
return subArray;
}
private static IEnumerable<Tuple<T, Int32>> IntersectIteratorWithIndex<T>(IEnumerable<T> first, IEnumerable<T> second)
{
var firstList = first.ToList();
var set = new Set<T>();
foreach (var i in second)
set.Add(i);
foreach (var i in firstList)
{
if (set.Remove(i))
yield return new Tuple<T, Int32>(i, firstList.IndexOf(i));
}
}
This is the optimal way, I found, to do this:
private void GetSubArrayThroughArraySegment() {
int[] array = { 10, 20, 30 };
ArraySegment<int> segment = new ArraySegment<int>(array, 1, 2);
Console.WriteLine("-- Array --");
int[] original = segment.Array;
foreach (int value in original)
{
Console.WriteLine(value);
}
Console.WriteLine("-- Offset --");
Console.WriteLine(segment.Offset);
Console.WriteLine("-- Count --");
Console.WriteLine(segment.Count);
Console.WriteLine("-- Range --");
for (int i = segment.Offset; i <= segment.Count; i++)
{
Console.WriteLine(segment.Array[i]);
}
}
Hope It Helps!
use extention method :
public static T[] Slice<T>(this T[] source, int start, int end)
{
// Handles negative ends.
if (end < 0)
{
end = source.Length + end;
}
int len = end - start;
// Return new array.
T[] res = new T[len];
for (int i = 0; i < len; i++)
{
res[i] = source[i + start];
}
return res;
}
and you can use it
var NewArray = OldArray.Slice(3,7);
Code from the System.Private.CoreLib.dll:
public static T[] GetSubArray<T>(T[] array, Range range)
{
if (array == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
(int Offset, int Length) offsetAndLength = range.GetOffsetAndLength(array.Length);
int item = offsetAndLength.Offset;
int item2 = offsetAndLength.Length;
if (default(T) != null || typeof(T[]) == array.GetType())
{
if (item2 == 0)
{
return Array.Empty<T>();
}
T[] array2 = new T[item2];
Buffer.Memmove(ref Unsafe.As<byte, T>(ref array2.GetRawSzArrayData()), ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), item), (uint)item2);
return array2;
}
T[] array3 = (T[])Array.CreateInstance(array.GetType().GetElementType(), item2);
Array.Copy(array, item, array3, 0, item2);
return array3;
}
array1 = [5,6,7,8];
int[] array2 = new int[2];
Array.ConstrainedCopy(array1, 1, array2, 0, 2);
array2 = [6,7];
Array.ConstrainedCopy takes five (5) parameters:
source array (sourceArray)
starting index of source array (sourceIndex)
destination array (destinationArray)
starting index of destination array (destinationIndex)
number of elements to copy (length)
public static T[] SubArray<T>(T[] data, int index, int length)
{
List<T> retVal = new List<T>();
if (data == null || data.Length == 0)
return retVal.ToArray();
bool startRead = false;
int count = 0;
for (int i = 0; i < data.Length; i++)
{
if (i == index && !startRead)
startRead = true;
if (startRead)
{
retVal.Add(data[i]);
count++;
if (count == length)
break;
}
}
return retVal.ToArray();
}

Categories

Resources