How to use BeginInvoke - c#

Ive got this problem:
Line 20 (LOOT_FromContainer(container) I need that to use as Invoke, because the process I want to exec takes some time, so ServerMessageHandler won't handle it...
If I just simply rewrite that line to LOOT_FromContainer.BeginInvoke(container); then I have this error:
error CS0119: 'LOOT.LOOT_FromContainer(Phoenix.Serial)' is a 'method',
which is not valid in the given context
I'm new to C#, came from PHP, and about Invoke I don't know much really. I've been trying to sort this out for a couple of days, not even google helped...
[ServerMessageHandler(0x3C)]
public CallbackResult ContainerContains(byte[] data, CallbackResult prevResult)
{
PacketReader reader = new PacketReader(data);
reader.Skip(3);
ushort len = reader.ReadUInt16();
for (int i = 0; i < len; i++)
{
Serial serial = (Serial)(reader.ReadUInt32());
ushort graphic = (ushort)(reader.ReadUInt16());
reader.Skip(7);
Serial container = (Serial)(reader.ReadUInt32());
ushort color = (ushort)(reader.ReadUInt16());
if (((int)graphic == 0x0E76) && ((int)color == 0x049A))
{
LOOT_FromContainer.BeginInvoke(container);
}
}
return CallbackResult.Normal;
}
[Command]
public static void LOOT_FromContainer(Serial target)
{
UOItem lootCorpse = new UOItem(target);
if (lootCorpse.Graphic == 0x2006)
{
if (((draw == 1) && (World.Player.Backpack.AllItems.Count(draw_knife[0], draw_knife[1]) > 0)) || (World.Player.Layers[Layer.RightHand].Exist))
{
if ((lootCorpse.Amount != 400) && (lootCorpse.Amount != 401))
{
if (draw == 0)
{
UO.WaitTargetObject(lootCorpse);
UO.UseObject(World.Player.Layers[Layer.RightHand].Serial);
}
else
{
UO.WaitTargetObject(lootCorpse);
UO.UseType(draw_knife[0], draw_knife[1]);
}
UO.Wait(500);
}
}
else
{
UO.Print("Neni cim rezat, pouze lootim");
}
for (int i = 0; i < loot.Length; i++)
{
if (lootCorpse.Items.Count(loot[i][0], loot[i][1]) > 0)
{
if (loot[i][2] == 1)
{
if (loot[i][4] == 1)
{
UO.MoveItem(lootCorpse.Items.FindType(loot[i][0], loot[i][1]), 0, Aliases.GetObject("loot_bag"), loot[i][5], loot[i][6]);
UO.Wait(200);
}
else
{
UO.MoveItem(lootCorpse.Items.FindType(loot[i][0], loot[i][1]), 0, World.Player.Backpack);
UO.Wait(200);
}
}
}
}
}
}

I think this is what you need. You need to declare a delegate with the same return type and input parameters as your method, instantiate this delegate pointing it at your method and then call BeginInvoke on it passing in your serial variable, followed by null, null:
public delegate void LFC(Serial target);
[ServerMessageHandler(0x3C)]
public CallbackResult ContainerContains(byte[] data, CallbackResult prevResult)
{
PacketReader reader = new PacketReader(data);
reader.Skip(3);
ushort len = reader.ReadUInt16();
for (int i = 0; i < len; i++)
{
Serial serial = (Serial)(reader.ReadUInt32());
ushort graphic = (ushort)(reader.ReadUInt16());
reader.Skip(7);
Serial container = (Serial)(reader.ReadUInt32());
ushort color = (ushort)(reader.ReadUInt16());
LFC = lootfromcontainer = new LFC(LOOT_FromContainer);
if (((int)graphic == 0x0E76) && ((int)color == 0x049A))
{
lootfromcontainer.BeginInvoke(container, null, null);
//LOOT_FromContainer.BeginInvoke(container);
}
}
return CallbackResult.Normal;
}
[Command]
public static void LOOT_FromContainer(Serial target)
{
UOItem lootCorpse = new UOItem(target);
if (lootCorpse.Graphic == 0x2006)
{
if (((draw == 1) && (World.Player.Backpack.AllItems.Count(draw_knife[0], draw_knife[1]) > 0)) || (World.Player.Layers[Layer.RightHand].Exist))
{
if ((lootCorpse.Amount != 400) && (lootCorpse.Amount != 401))
{
if (draw == 0)
{
UO.WaitTargetObject(lootCorpse);
UO.UseObject(World.Player.Layers[Layer.RightHand].Serial);
}
else
{
UO.WaitTargetObject(lootCorpse);
UO.UseType(draw_knife[0], draw_knife[1]);
}
UO.Wait(500);
}
}
else
{
UO.Print("Neni cim rezat, pouze lootim");
}
for (int i = 0; i < loot.Length; i++)
{
if (lootCorpse.Items.Count(loot[i][0], loot[i][1]) > 0)
{
if (loot[i][2] == 1)
{
if (loot[i][4] == 1)
{
UO.MoveItem(lootCorpse.Items.FindType(loot[i][0], loot[i][1]), 0, Aliases.GetObject("loot_bag"), loot[i][5], loot[i][6]);
UO.Wait(200);
}
else
{
UO.MoveItem(lootCorpse.Items.FindType(loot[i][0], loot[i][1]), 0, World.Player.Backpack);
UO.Wait(200);
}
}
}
}
}
}

If you simply need to run that in another thread you can use ThreadPool.
For this you would need few easy things:
Instead of:
LOOT_FromContainer.BeginInvoke(container);
You would use:
ThreadPool.QueueUserWorkItem(LOOT_FromContainer, container);
And slightly modify your LOOT_FromContainer method to:
public static void LOOT_FromContainer(object prm)
{
var target = (Serial)prm;
// ...

I'm assuming LOOT_FromCotainer is a control, and given the error your compiler returns, one can roughly guess the problem is that you're calling BeginInvoke with a non-delegate. BeginInvoke is called as follows:
LOOT_FromContainer.BeginInvoke(container); //where container is a delegate that maybe declared as follows
private delegate void container; //may also contain parameters eg container(string s);
So just rework your code to follow in this manner. See this document for example on how to use BeginInvoke.

Related

A simple iteration with if else

Iterating through an array of strings i want it to write a specific line if one of the elements coresponds to the condition. The problem is with the else condition. It is written as many times as the length of the array and i only need it written once
public static void FindSandy(params string[] ocean)
{
for (int i = 0; i < ocean.Length; i++)
{
if (ocean[i] == "Sandy")
{
Console.WriteLine("We found Sandy on position {0}", i);
}
else
{
Console.WriteLine("He was not here");
}
}
}
static void Main(string[] args)
{
{
FindSandy("Bob","Bella", "Sandy", "Nemo", "Dory");
}
}
What about if you just return if you found it?
public static void FindSandy(params string[] ocean)
{
for (int i = 0; i < ocean.Length; i++)
{
if (ocean[i] == "Sandy")
{
Console.WriteLine("We found Sandy on position {0}", i);
// Found, you can return from method.
return;
}
}
// Not found, write the 'not found' message.
Console.WriteLine("He was not here");
}
The simplest way to change your code to handle this is to create a variable that tracks the index where Sandy is found, initialize it to an invalid value (like -1), and then set it to the actual value in your if block (and we can also add a break; statement to exit the loop as soon as we find him).
Then, we output a string based on the value of the position variable:
public static void FindSandy(params string[] ocean)
{
int position = -1;
for (int i = 0; i < ocean?.Length; i++)
{
if (ocean[i] == "Sandy")
{
position = i;
break;
}
}
if (position > -1)
{
Console.WriteLine("We found Sandy on position {0}", position);
}
else
{
Console.WriteLine("He was not here");
}
}
The code can be simplified a little with the System.Linq extension methods Select (to select the name and then index) and FirstOrDefault which returns the first item that meets a condidion, or the default for the type (which is null):
public static void FindSandy(params string[] ocean)
{
var position = ocean?.Select((name, index) => new {name, index})
.FirstOrDefault(item => item.name == "Sandy");
Console.WriteLine(position == null
? "He was not here"
: $"We found Sandy on position {position.index}");
}
You can use the keyword break to exit the for loop :
public static void FindSandy(params string[] ocean)
{
for (int i = 0; i < ocean.Length; i++)
{
if (ocean[i] == "Sandy")
{
Console.WriteLine("We found Sandy on position {0}", i);
break;
}
else if (i == ocean.Length - 1)
{
Console.WriteLine("He was not here");
break;
}
}
}
To solve your issue, you could add a new boolean variable (e.g. weFoundSandy): if you find an occurrence, set this variable to true, use the break statement (to reduce the iterations of the for) and, at the end, use this boolean variable to determine which message to display.
public static void FindSandy(params string[] ocean) {
bool weFoundSandy = false;
for (int i = 0; i < ocean.Length; i++) {
if (ocean[i] == "Sandy") {
Console.WriteLine("We found Sandy on position {0}", i);
weFoundSandy = true;
break;
}
}
if (!weFoundSandy) {
Console.WriteLine("Sandy was not here");
}
}
or, you could simply use the C# Array.IndexOf method, e.g.:
public static void FindSandy(params string[] ocean) {
int indexOfSandy = Array.IndexOf(ocean, "Sandy");
if (indexOfSandy >= 0) {
Console.WriteLine("We found Sandy on position {0}", indexOfSandy);
} else {
Console.WriteLine("Sandy was not here");
}
}

How can I improve this logic? C#

I have a library that returns CS:GO stats in real-time from the game. I'm making a program that stores the stats and analyse it.
I have this function:
private void UpdateKills(GameState gs)
{
int currentKills = -1;
if (lastKills == -1) // first time getting player info
{
int temp = gs.Player.MatchStats.Kills;
currentKills = temp;
lastKills = temp;
}
else
{
currentKills = gs.Player.MatchStats.Kills;
int dif = currentKills - lastKills;
if (currentKills == 0 && lastKills != 0) // maybe changed server/map/whatever
{
lastKills = -1;
}
else
{
if (dif != 0 && dif > 0) // player killed someone AND it was not teamkill
{
ps.Kills += dif; // add the kills to the main variable
lastKills = currentKills;
dif = 0;
playSimpleSound();
}
}
}
}
This is my function that handles the kills. The most of the time it works very well, but sometimes it just freaks out, and I don't know if the problem is my logic or if it is a library problem.
Note: I'm using this library: github.com/rakijah/CSGSI
My logic is:
Get the player kills
Increment those kills in the PlayerStats object.
Is my code logically correct? Can my code be more "correct"?
I'm still not entirely sure what this function is supposed to be doing, but I simplified it for you:
private void UpdateKills(GameState gs)
{
lastKills = currentKills;
int currentKills = gs.Player.MatchStats.Kills;
int diff = currentKills - lastKills;
if (currentKills == 0 && lastKills != 0) // maybe changed server/map/whatever
{
lastKills = -1;
}
else if (diff > 0) // player killed someone AND it was not teamkill
{
ps.Kills += diff; // add the kills to the main variable
playSimpleSound();
}
}
Well, after some research in finally undertand your problem. You DON'T have to do anything!, the API does all the work for you, watch the next image:
As you can see, the console app shows my steam name, the map and kills. I start with zero kills, then I kill a teammate and after that an enemy. The API created by #rakijah autoupdates those values.
Now the code:
static void Main(string[] args)
{
CsGoIntegration();
}
private static void CsGoIntegration()
{
var gsl = new GameStateListener(3000);
gsl.NewGameState += new NewGameStateHandler(OnNewGameState);
if (!gsl.Start())
{
Environment.Exit(0);
}
System.Console.WriteLine("Listening...");
}
private static void OnNewGameState(GameState gs)
{
System.Console.WriteLine("Map: {0}", gs.Map.Name);
System.Console.WriteLine("Player Name: {0}", gs.Player.Name);
System.Console.WriteLine("Player Kills: {0}", gs.Player.MatchStats.Kills);
}
UPDATE: The OP needs to store the total kills even when the map changes. I experimented with paper and pencil, please try running the program and tell me if it worked or not
private static void Main()
{
CsGoIntegration();
}
private static void CsGoIntegration()
{
var gsl = new GameStateListener(3000);
gsl.NewGameState += OnNewGameState;
if (!gsl.Start())
{
Environment.Exit(0);
}
Console.WriteLine("Listening...");
}
private static void OnNewGameState(GameState gameState)
{
SaveMatchsData(gameState);
}
private static int? _totalKillScore;
private static string _lastMapName;
private static int? _lastKillScore;
private static void SaveMatchsData(GameState gameState)
{
const string undefinedString = "Undefined";
// If the SaveMatchsData is running and the CSGO server is offline
if (gameState.Map.Name == undefinedString && string.IsNullOrEmpty(_lastMapName))
return;
// When the match is not started, the Round is -1
if (gameState.Map.Name != undefinedString && gameState.Map.Round > -1)
{
if (string.IsNullOrEmpty(_lastMapName))
{
UpdateData(gameState, true);
}
else
{
// Same map
if (_lastMapName == gameState.Map.Name)
{
// Check if the Score Changes
if (_lastKillScore == gameState.Player.MatchStats.Kills) return;
UpdateData(gameState);
}
// The Map Changes
else
{
UpdateData(gameState, true);
}
}
}
}
private static void UpdateData(GameState gameState, bool updateMap = false)
{
if (updateMap)
_lastMapName = gameState.Map.Name;
_lastKillScore = gameState.Player.MatchStats.Kills;
_totalKillScore += gameState.Player.MatchStats.Kills;
}
Cheers.

Count Number of Functions in c programme using C#

How can i count number of functions in C program File using C# program? I have create a simple C# class to count the LOC in C file.
private bool IsInMultipleComment = false;
private int getNumberOFFuncions(FileInfo fs)
{
StreamReader rdr;
int count = 0;
string tempStr;
// initialize
rdr = fs.OpenText();
tempStr = rdr.ReadLine();
while (true)
{
if (tempStr == null)
break;
if (IsFunction(tempStr))
count++;
tempStr = rdr.ReadLine();
}
return count;
}
Supportive method:
private bool IsFunction(string line)
{
if (line.Contains("//"))
return false;
if (line.Contains("/*"))
IsInMultipleComment = true;
if (line.Contains("*/"))
IsInMultipleComment = false;
if (!IsInMultipleComment)
{
if (line.Contains("void") || line.Contains("int") || line.Contains("short") || line.Contains("long") || line.Contains("float") || line.Contains("char") || line.Contains("double"))
{
if (!line.Contains(";"))
{
return true;
}
}
}
return false;
}
This is how I count variables:
private int getNumberOfVariables(FileInfo fs)
{
StreamReader rdr;
int count = 0;
string tempStr;
// initialize
rdr = fs.OpenText();
tempStr = rdr.ReadLine();
while (true)
{
if (tempStr == null)
break;
count += getVariblesOfLine(tempStr);
tempStr = rdr.ReadLine();
}
return count;
}
Supportive method:
private int getVariblesOfLine(string line)
{
line = line.Trim(); // trim the lines
if (line.Contains("#")) // remove preprocessive declarations
return 0;
if (line.Contains("//"))
return 0;
if (line.Contains("/*"))
IsInMultipleComment = true;
if (line.Contains("*/"))
IsInMultipleComment = false;
if (!IsInMultipleComment)
{
if (line.Contains("unsigned") || line.Contains("signed") || line.Contains("int") || line.Contains("short") || line.Contains("long") || line.Contains("float") || line.Contains("char") || line.Contains("double"))
{
if (!line.Contains("(")) // remove if this is function
{
Console.WriteLine(line);
if (line.Contains(",")) // count at multiple declarations
{
int y = line.Count(f => f == ',');
return y + 1;
}
return 1;
}
}
}
return 0;
}
Learn about Regex(s). There is a string pattern what a function declaration looks like. You won't catch every possible contortion of a function, you can get most of them, if you use declare functions in the general accepted .net way. Expresso is learning good learning tool for helping you to get the pattern of the Regex.
Here is a pattern to identify a function. It looks crazy but it's not. Expresso will decode it for you. It's not fully developed in that it won't catch private functions where you don't put the word private in front of it and it doesn't do protected internal. There is probably many more that it won't catch.
Regex regex = new Regex("\s*(private|public|internal|protected)\s*\w+\s+([a-zA-Z_0-9.]+)\s*\(.*\)",RegexOptions.Compiled)
if (regex.IsMatch(lineOfCode)
{
//then it's a function
}
On another note, don't keep opening and re-reading the file. Open it once, make a pass, that's it.
I've got some code (in javascript) to do line counts and such on Csharp files you might be able to pull out some of the regex patterns. Note how the regexes are kept in an object (dictionary in .net) In javascript, /pattern/ is the same as .net "pattern"
module.exports = ( function() {
var classifiers = [] ;
classifiers.push(
{
ctype: "using",
regex: /^(\s*using\s*)([a-zA-Z_0-9.]+)/,
extractMethod: function(lineInfo) {
lineInfo.extractValue = lineInfo.line.split(this.regex)[2] ;
}
},
{
ctype: "namespace",
regex: /^(\s*namespace\s*)([a-zA-Z_0-9.]+)/,
extractMethod: function(lineInfo) {
lineInfo.extractValue = lineInfo.line.split(this.regex)[2] ;
}
},
{
ctype: "comment",
regex: /^\s*\/\/[/ A-Za-z,*]*/,
extractMethod: function(lineInfo) {
lineInfo.extractValue = null ;
}
},
{
ctype: "n/a",
regex: /^\s*$|^\s*[;{}]+?\s*$/,
extractMethod: function(lineInfo) {
lineInfo.extractValue = null ;
}
}
);
function classifyLine(line, lineNo) {
var lineInfo = {} ;
lineInfo.line = line ;
lineInfo.lineNo = lineNo;
for (var index = 0; index < classifiers.length; index++) {
var classifier = classifiers[index];
if (classifier.regex.test(line)) {
lineInfo.ctype = classifier.ctype;
lineInfo.line = line ;
classifier.extractMethod(lineInfo) ;
break ;
}
}
if (lineInfo.ctype == undefined){
lineInfo.ctype = "code" ;
}
return lineInfo ;
}
return {
classifyLine : classifyLine
};
} )();

Postfix increment into if, c#

Code example:
using System;
public class Test {
public static void Main() {
int a = 0;
if(a++ == 0){
Console.WriteLine(a);
}
}
}
In this code the Console will write: 1. I can write this code in another way:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
These two examples work exactly the same (from what I know about postfix).
The problem is with this example coming from the Microsoft tutorials:
using System;
public class Document {
// Class allowing to view the document as an array of words:
public class WordCollection {
readonly Document document;
internal WordCollection (Document d){
document = d;
}
// Helper function -- search character array "text", starting
// at character "begin", for word number "wordCount". Returns
//false if there are less than wordCount words. Sets "start" and
//length to the position and length of the word within text
private bool GetWord(char[] text, int begin, int wordCount,
out int start, out int length) {
int end = text.Length;
int count = 0;
int inWord = -1;
start = length = 0;
for (int i = begin; i <= end; ++i){
bool isLetter = i < end && Char.IsLetterOrDigit(text[i]);
if (inWord >= 0) {
if (!isLetter) {
if (count++ == wordCount) {//PROBLEM IS HERE!!!!!!!!!!!!
start = inWord;
length = i - inWord;
return true;
}
inWord = -1;
}
} else {
if (isLetter) {
inWord = i;
}
}
}
return false;
}
//Indexer to get and set words of the containing document:
public string this[int index] {
get
{
int start, length;
if(GetWord(document.TextArray, 0, index, out start,
out length)) {
return new string(document.TextArray, start, length);
} else {
throw new IndexOutOfRangeException();
}
}
set {
int start, length;
if(GetWord(document.TextArray, 0, index, out start,
out length))
{
//Replace the word at start/length with
// the string "value"
if(length == value.Length){
Array.Copy(value.ToCharArray(), 0,
document.TextArray, start, length);
}
else {
char[] newText = new char[document.TextArray.Length +
value.Length - length];
Array.Copy(document.TextArray, 0, newText, 0, start);
Array.Copy(value.ToCharArray(), 0, newText, start, value.Length);
Array.Copy(document.TextArray, start + length, newText,
start + value.Length, document.TextArray.Length - start - length);
document.TextArray = newText;
}
} else {
throw new IndexOutOfRangeException();
}
}
}
public int Count {
get {
int count = 0, start = 0, length = 0;
while (GetWord(document.TextArray, start + length,
0, out start, out length)) {
++count;
}
return count;
}
}
}
// Class allowing the document to be viewed like an array
// of character
public class CharacterCollection {
readonly Document document;
internal CharacterCollection(Document d) {
document = d;
}
//Indexer to get and set character in the containing
//document
public char this[int index] {
get {
return document.TextArray[index];
}
set {
document.TextArray[index] = value;
}
}
//get the count of character in the containing document
public int Count {
get {
return document.TextArray.Length;
}
}
}
//Because the types of the fields have indexers,
//these fields appear as "indexed properties":
public WordCollection Words;
public readonly CharacterCollection Characters;
private char[] TextArray;
public Document(string initialText) {
TextArray = initialText.ToCharArray();
Words = new WordCollection(this);
Characters = new CharacterCollection(this);
}
public string Text {
get {
return new string(TextArray);
}
}
class Test {
static void Main() {
Document d = new Document(
"peter piper picked a peck of pickled peppers. How many pickled peppers did peter piper pick?"
);
//Change word "peter" to "penelope"
for(int i = 0; i < d.Words.Count; ++i){
if (d.Words[i] == "peter") {
d.Words[i] = "penelope";
}
}
for (int i = 0; i < d.Characters.Count; ++i) {
if (d.Characters[i] == 'p') {
d.Characters[i] = 'P';
}
}
Console.WriteLine(d.Text);
}
}
}
If I change the code marked above to this:
if (count == wordCount) {//PROBLEM IS HERE
start = inWord;
length = i - inWord;
count++;
return true;
}
I get an IndexOutOfRangeException, but I don't know why.
Your initial assumption is incorrect (that the two examples work exactly the same). In the following version, count is incremented regardless of whether or not it is equal to wordCount:
if (count++ == wordCount)
{
// Code omitted
}
In this version, count is ONLY incremented when it is equal to wordCount
if (count == wordCount)
{
// Other code omitted
count++;
}
EDIT
The reason this is causing you a failure is that, when you are searching for the second word (when wordCount is 1), the variable count will never equal wordCount (because it never gets incremented), and therefore the GetWord method returns false, which then triggers the else clause in your get method, which throws an IndexOutOfRangeException.
In your version of the code, count is only being incremented when count == wordCount; in the Microsoft version, it's being incremented whether the condition is met or not.
using System;
public class Test {
public static void Main() {
int a = 0;
if(a++ == 0){
Console.WriteLine(a);
}
}
}
Is not quite the same as:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
In the second case a++ is executed only if a == 0. In the first case a++ is executed every time we check the condition.
There is your mistake:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
It should be like this:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
else
a++;
}
a gets alwasy increased. This means, that in your code example count will get only increased when count == wordCount (In which case the method will return true anyway...). You basicly never increasing count.

Converting `Int32` to `T` and `T []` to `Int32 []` in C#

I was attempting to write a simple function that would use the RandomNumberGenerator class to return an array of Int16, Int32 or Int64 based on a generic argument.
However, no matter how I try to structure the code, I cannot seem to get past the illegal conversion from T [] to short/int/long [], nor the conversion from IntXX to T. Please see the two comments in the code below.
It seems I am missing a basic construct that would allow a way around this. Any thoughts?
public static void GenerateRandom<T> (T [] data, bool nonZeroOnly = false)
where T: struct, System.IComparable, System.IFormattable, System.IConvertible
{
int size = 0;
byte [] bytes = null;
if ((typeof(T) != typeof(byte)) && (typeof(T) != typeof(short)) && (typeof(T) != typeof(int)) && (typeof(T) != typeof(long)))
{
throw (new System.ArgumentException("This method only accepts types [Byte], [Int16], [Int32], or [Int64].", "<T>"));
}
if (typeof(T) == typeof(byte))
{
using (System.Security.Cryptography.RandomNumberGenerator generator = System.Security.Cryptography.RandomNumberGenerator.Create())
{
// Invalid cast (implicit or explicit) from T [] to byte [].
if (nonZeroOnly) { generator.GetNonZeroBytes(data); }
else { generator.GetBytes(data); }
}
}
else
{
size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
bytes = new byte [data.Length * size];
using (System.Security.Cryptography.RandomNumberGenerator generator = System.Security.Cryptography.RandomNumberGenerator.Create())
{
if (nonZeroOnly) { generator.GetNonZeroBytes((byte []) System.Convert.ChangeType(data, typeof(byte []))); }
else { generator.GetBytes((byte []) System.Convert.ChangeType(data, typeof(byte []))); }
}
using (System.IO.MemoryStream stream = new System.IO.MemoryStream(bytes))
{
using (System.IO.BinaryReader reader = new System.IO.BinaryReader(stream))
{
// Invalid cast (implicit or explicit) from short/int/long to T.
if (typeof(T) == typeof(short)) { for (int i=0; i<bytes.Length; i+=size) { data[i] = reader.ReadInt16(); } }
else if (typeof(T) == typeof(int)) { for (int i=0; i<bytes.Length; i+=size) { data[i] = reader.ReadInt32(); } }
else if (typeof(T) == typeof(long)) { for (int i=0; i<bytes.Length; i+=size) { data[i] = reader.ReadInt64(); } }
}
}
}
}
On a side note, is there a more efficient way of converting a byte [] to a IntXX [] without using a stream and binary reader?
You can do this, to get from int to T:
void Foo<T>(T[] data)
{
...
int v = r.Next(255); // limit to byte.max for simplicity
data[i] = (T) Convert.ChangeType(v, typeof(T));
}
I think you are trying to be clever here and in the process making things far too complex. Just write threee separate methods:
Int16[] GenerateRandomShorts()
Int32[] GenerateRandomInts()
Int64[] GenerateRandomLongs()
There is absolutely no reason to use generics here, and the problems you are facing are the result of swimming against the current.
Simply use non-generic overloads and let the compiler pick the one to use based on the type of the first argument:
public static void GenerateRandom(byte[] data, bool nonZeroOnly = false)
{
using (var generator = RandomNumberGenerator.Create())
{
if (nonZeroOnly) { generator.GetNonZeroBytes(data); }
else { generator.GetBytes(data); }
}
}
public static void GenerateRandom(short[] data, bool nonZeroOnly = false)
{
var size = sizeof(short);
var bytes = new byte[data.Length * size];
GenerateRandom(bytes, nonZeroOnly);
for (var i = 0; i < data.Length; ++i) {
data[i] = BitConverter.ToInt16(bytes, i * size);
}
}
Two more overloads like the last one would take care of int and long.

Categories