Ho un array X di 10 elementi. Vorrei creare un nuovo array contenente tutti gli elementi di X che iniziano con l’indice 3 e terminano con l’indice 7. Certo, posso facilmente scrivere un ciclo che lo farà per me, ma vorrei mantenere il mio codice il più pulito ansible . C’è un metodo in C # che può farlo per me?
Qualcosa come (pseudo codice):
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)
Array.Copy
non si adatta alle mie esigenze . Ho bisogno che gli elementi nel nuovo array siano cloni. Array.copy
è solo un equivalente memcpy
stile C, non è quello che sto cercando.
Puoi aggiungerlo come metodo di estensione:
public static T[] SubArray(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} }
Aggiornamento della clonazione (che non era ovvio nella domanda originale). Se vuoi davvero un clone profondo; qualcosa di simile a:
public static T[] SubArrayDeepClone(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); } }
Ciò richiede che gli oggetti siano serializzabili ( [Serializable]
o ISerializable
), comunque. Potresti facilmente sostituire qualsiasi altro serializzatore come appropriato: XmlSerializer
, DataContractSerializer
, protobuf-net, ecc.
Nota che il clone profondo è complicato senza serializzazione; in particolare, ICloneable
è difficile da fidarsi nella maggior parte dei casi.
È ansible utilizzare Array.Copy(...)
per copiare nel nuovo array dopo averlo creato, ma non penso che esista un metodo che crea il nuovo array e copia un intervallo di elementi.
Se stai usando .NET 3.5, puoi usare LINQ:
var newArray = array.Skip(3).Take(5).ToArray();
ma sarà un po ‘meno efficiente.
Vedi questa risposta a una domanda simile per opzioni per situazioni più specifiche.
Hai preso in considerazione l’utilizzo di ArraySegment
?
Vedo che vuoi fare la clonazione, non solo copiare i riferimenti. In questo caso puoi usare. Seleziona per proiettare i membri dell’array ai loro cloni. Ad esempio, se i tuoi elementi hanno implementato IClonable potresti fare qualcosa del genere:
var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();
Il seguente codice lo fa in una riga:
// Source array string[] Source = new string[] { "A", "B", "C", "D" }; // Extracting a slice into another array string[] Slice = new List(Source).GetRange(2, 2).ToArray();
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" }; arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
Basandosi sulla risposta di Marc, ma aggiungendo il comportamento desiderato di clonazione
public static T[] CloneSubArray(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; }
E se l'implementazione di ICloneable è troppo simile a quella del lavoro duro, è ansible utilizzare una libreria riflettente di Håvard Stranden per eseguire il pesante lavoro richiesto.
using OX.Copyable; public static T[] DeepCopySubArray( 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; }
Si noti che l'implementazione di OX.Copyable funziona con uno di:
Perché la copia automatizzata funzioni, tuttavia, una delle seguenti affermazioni deve contenere per esempio:
- Il suo tipo deve avere un costruttore senza parametri, o
- Deve essere un Copiabile, o
- Deve avere un IInstanceProvider registrato per il suo tipo.
Quindi questo dovrebbe coprire quasi tutte le situazioni che hai. Se stai clonando oggetti in cui il sotto-grafo contiene cose come connessioni db o handle di file / stream hai ovviamente dei problemi, ma è vero per qualsiasi deep copy generalizzata.
Se si desidera utilizzare un altro approccio di copia profonda, questo articolo ne elenca molti altri, quindi suggerirei di non provare a scriverne uno personale.
Puoi farlo abbastanza facilmente;
object[] foo = new object[10]; object[] bar = new object[7]; Array.Copy(foo, 3, bar, 0, 7);
Penso che il codice che stai cercando sia:
Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)
Array.ConstrainedCopy funzionerà.
public static void ConstrainedCopy ( Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length )
In alternativa alla copia dei dati, puoi creare un wrapper che ti consente di accedere a una parte dell’array originale come se fosse una copia della parte dell’array. Il vantaggio è che non si ottiene un’altra copia dei dati in memoria e l’inconveniente è un leggero sovraccarico quando si accede ai dati.
public class SubArray : IEnumerable { 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 GetEnumerator() { for (int i = 0; i < Length; i++) { yield return _original[_start + i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
Uso:
int[] original = { 1, 2, 3, 4, 5 }; SubArray copy = new SubArray (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
Non esiste un unico metodo che farà ciò che vuoi. Dovrai rendere disponibile un metodo clone per la class nel tuo array. Quindi, se LINQ è un’opzione:
Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray(); class Foo { public Foo Clone() { return (Foo)MemberwiseClone(); } }
Che ne dici di utilizzare 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);
È ansible utilizzare 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
Cosa ne pensi di questo:
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; }
È quindi necessario implementare l'interfaccia ICloneable su tutte le classi necessarie per l'utilizzo ma ciò dovrebbe essere fatto.
Non sono sicuro di quanto sia veramente profondo, ma:
MyArray.ToList
È un po ‘sovraccarico, ma potrebbe tagliare un metodo non necessario.
Per quanto riguarda la clonazione, non penso che la serializzazione chiama i tuoi costruttori. Questo potrebbe violare gli invarianti di class se stai facendo cose interessanti nel ctor.
Sembra che la scommessa più sicura siano i metodi clone virtuali che chiamano i costruttori di copie.
protected MyDerivedClass(MyDerivedClass myClass) { ... } public override MyBaseClass Clone() { return new MyDerivedClass(this); }
La clonazione di elementi in un array non è qualcosa che può essere fatto in modo universale. Vuoi una clonazione profonda o una semplice copia di tutti i membri?
Andiamo per l’approccio “best effort”: clonazione di oggetti usando l’interfaccia ICloneable o serializzazione binaria:
public static class ArrayExtensions { public static T[] SubArray(this T[] array, int index, int length) { T[] result = new T[length]; for (int i=index;i
Questa non è una soluzione perfetta, perché semplicemente non c'è nessuno che funzioni per qualsiasi tipo di object.
Puoi prendere lezioni fatte da Microsoft:
internal class Set { private int[] _buckets; private Slot[] _slots; private int _count; private int _freeList; private readonly IEqualityComparer _comparer; public Set() : this(null) { } public Set(IEqualityComparer comparer) { if (comparer == null) comparer = EqualityComparer .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; } }
e poi
public static T[] GetSub(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> IntersectIteratorWithIndex (IEnumerable first, IEnumerable second) { var firstList = first.ToList(); var set = new Set (); foreach (var i in second) set.Add(i); foreach (var i in firstList) { if (set.Remove(i)) yield return new Tuple(i, firstList.IndexOf(i)); } }
Questo è il modo ottimale, ho trovato, per fare questo:
private void GetSubArrayThroughArraySegment() { int[] array = { 10, 20, 30 }; ArraySegment segment = new ArraySegment (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]); } }
Spero che sia d'aiuto!
public static T[] SubArray(T[] data, int index, int length) { List retVal = new List (); 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(); }