2016-10-15 3 views
1

Ich weiß, der Titel der Frage ist nicht sehr klar, aber lassen Sie mich versuchen, so gut wie möglich zu erklären.Verwenden Sie "Params" Schlüsselwort, um einzelne Werte sowie eine Wertesammlung zu enthalten

Ich arbeite an einer Anwendung, die Bytes einer Datei ändert. Ich habe zwei Klassen dafür: Mod und Action.

Ein Action in einer Instanz von Mod gespeichert wird, teilt dies die Anwendung, die auf die spezifische Adresse zu ändern Bytes

public class Action 
{ 
    public Action(int address, params byte[] bytes) 
    { 
     Address = address; 
     Bytes = bytes; 
    } 

    public int Address { get; } 
    public byte[] Bytes { get; } 
} 
public class Mod 
{ 
    public Mod(string name, List<Action> actions) 
    { 
     Name = name; 
     Actions = actions; 
    } 

    public string Name { get; } 
    public List<Action> Actions { get; } 
} 

Nun gibt einige Bytes sind, die ich will, schreiben, die eher repetitiven werden , so statt sie zu bytes zuzuweisen, wenn eine Instanz Action Erstellen I gespeichert haben die Byte-Array innerhalb einer statischen Klasse OpCodes genannt:

public static class OpCodes 
{ 
    public static byte[] BX_LR = {0x70, 0x47}; 
} 

So, jetzt, was ich versuche zu erreichen ist folgendes:

new Action(0x1AF0F0, 0x01, 0x20, OpCodes.BX_LR) //Return 1. (true) 

Wie Sie sehen können, ich will 0x01 Bytes verwenden, 0x20 und dann möchte ich auch den Wert von BX_LR jedoch verwenden, der Compiler mag das nicht.

ich dieses:

Argument 4: Konvertierung von 'byte []' auf 'byte'

Wie kann ich das erreichen?

+1

Compiler mag das nicht? –

+0

Ich fügte hinzu, was der Compiler sagt. – ThePerplexedOne

+1

Es gibt bereits eine 'Action'-Klasse im Framework. Vielleicht möchten Sie das ändern, aber es ist nicht notwendig! – pid

Antwort

2

I conjuted dies in LinqPad:

void Main() 
{ 
    var x = new Action(0x1AF0F0, 0x01, 0x20, OpCode.BX_LR); //Return 1. (true) 
    Console.WriteLine(x); 
} 

public class Action 
{ 
    public Action(int address, params OpCode[] bytes) 
    { 
     Address = address; 
     Bytes = OpCode.Flatten(bytes); 
    } 

    public int Address { get; set; } 
    public byte[] Bytes { get; set; } 
} 

public class Mod 
{ 
    public Mod(string name, List<Action> actions) 
    { 
     Name = name; 
     Actions = actions; 
    } 

    public string Name { get; set; } 
    public List<Action> Actions { get; set; } 
} 

public class OpCode 
{ 
    private byte[] _value; 

    private OpCode(byte[] value) 
    { 
     _value = value; 
    } 

    public static byte[] BX_LR = {0x70, 0x47}; 

    public static implicit operator OpCode (byte value) 
    { 
     return new OpCode(new []{value}); 
    } 

    public static implicit operator OpCode (byte[] value) 
    { 
     return new OpCode(value); 
    } 

    public static byte[] Flatten (OpCode[] opCodes) 
    { 
     var result = new List<byte>(opCodes.Length); 
     foreach (var opCode in opCodes) 
     { 
      result.AddRange(opCode._value); 
     } 
     return result.ToArray(); 
    } 
} 

Der Ausgang in LinqPad ist wie folgt:

Address 
1765616 
Bytes 
01 20 70 47 

Der Code, der die Klasse verwendet OpCode (I OpCodes zu singulären umbenannt) als Vermittler zwischen byte[] und byte. Es macht eine schöne Invokation.

Die Idee ist, dass der Konstruktor von Action ein params von OpCode und jede byte oder byte[] zu OpCode implizit gegossen wird nimmt, so dass Sie nur Bytes in dem Aufruf setzen können ... dann in der constructure können Sie flache Struktur zu eine einzelne byte[] mit einem dedizierten Anruf.

Hinweis: Ich habe zu den Eigenschaften wegen LinqPad's beschwert hinzugefügt.


Die Vorteile oder diese sind in der Lesbarkeit des Anrufs, noch, wie Sie es sehen können, ein Overhead ist ... das Overhead nur Sinn für lange Reihe von Bytes machen.

Btw.Sie können _value readonly machen, würde ich auch vorschlagen, OpCode sealed ein struct zu machen. Der Grund ist, dass unter Verwendung von OpCode, wie im Code dargestellt, eine Reihe von kurzlebigen Objekten erstellt werden, aber wenn sie struct sind, werden sie den Garbage Collector nicht laden. Lassen Sie auch keine Derivat-Typen zu. Bearbeiten: Nachteil der Struktur ist, dass Sie den Standardkonstruktor nicht steuern können, und das wird es mit _values als null initialisieren.

Ein weiterer Vorschlag, um die öffentlichen statischen Felder als readonly zu definieren (nicht zulassen, sie zu ersetzen) und OpCode, die eine Umwandlung speichern und verhindern wird, das Array zu manipulieren.

+0

Erstaunlich! Ich danke dir sehr. – ThePerplexedOne

+1

@ThePerplexedOne noch eine Sache, wird dies eine Reihe von kurzlebigen Objekten erstellen. Der .NET-Garbage Collector ist dafür gut, aber wenn es jemals ein Problem wird, können Sie ein Array mit den 256 einzelnen Byte 'OpCodes' behalten und den impliziten Cast durch Array-Lookup implementieren. Edit: Es ist mir nur aufgefallen, dass die Verwendung von struct das viel besser lösen wird. – Theraot

+0

Für die Verwendung von implizitem Casting wurde etwas hochgestuft, was nicht sehr oft zu sehen ist. – pid

1

würde ich eine Verlängerung Methode verwenden, um prepend/anhängen diese Bytes auf ein Array:

public static class Extensions 
{ 
    public static byte[] Prepend(this byte[] that, params byte[] data) 
    { 
     byte[] modified; 

     modified = new byte[that.Length + data.Length]; 
     Array.Copy(data, modified, data.Length); 
     Array.Copy(that, 0, modified, data.Length, that.Length); 

     return modified; 
    } 

    public static byte[] Append(this byte[] that, params byte[] data) 
    { 
     byte[] modified; 

     modified = new byte[that.Length + data.Length]; 
     Array.Copy(that, modified, that.Length); 
     Array.Copy(data, 0, modified, that.Length, data.Length); 

     return modified; 
    } 
} 

Sie können nun sie wie folgt verwendet werden:

new Action(0x1AF0F0, OpCodes.BX_LR.Prepend(0x01, 0x20)) 

Oder diese:

new Action(0x1AF0F0, OpCodes.BX_LR.Append(0x01, 0x20)) 
Verwandte Themen