I'm accessing a value object as follows: accounts[accountNum]
I want to do some minor manipulation on this object, ie: increase it by something, etc. Rather than typing "accounts[accountNum] += something" I'd rather do something like this:
MemberData account = accounts[accountNum];
account.balance += something;
Ie: "account" should be a direct pointer, reference, or somehow "be equivalent to" typing "accounts[accountNum]". But since this is a value type and not a reference type, it makes a copy and I cannot manipulate that object directly.
All my research on using "ref" seems to talk about using it within method parameters and return types, not in variables. Is there a simple way to shortcut a long variable name whilst still referencing the same object?
UPDATE:
Full source code - Please see specifically Line 46 & 51
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
/* * Description of the project:
* To implement an ATM machine which accepts deposits, withdrawals
* DEPOSIT / WITHDRAWAL:
* Account, Amount, Timestamp, TransactionID, Location
* Keep record of all accounts in TXT file, which is updated after each transaction. Of course in live system DB would be used.
* Keep another file that logs all transactions. Make it possible to LINQ the log associated with account, to show transaction history.
* TXT Accounts in form of:
* Account #, Name, Balance
* */
namespace ATMProject
{
class Program
{
static void Main(string[] args)
{
// Globals.DisplayAccounts();
// Deposit.MakeDeposit(1000009, 500, "sydney");
}
}
static class Deposit
{
//TODO Deposit: * Account #, Amount, Timestamp, TransactionID, Location
public static void MakeDeposit(long accountNum, long amount, string location)
{
string timestamp;
string transactionID;
timestamp = DateTime.Now.ToString("yyyyMMddhhmm");
transactionID = Globals.GenerateValidTransactionID();
using (StreamWriter sr = new StreamWriter(Globals.transactions, true))
{
string transactString = $"DEPOSIT| {accountNum}| {amount}| {timestamp}| {transactionID}| {location}";
sr.WriteLine(transactString);
}
Globals.WriteTransactionID(transactionID);
Dictionary<long, MemberData> accounts = Globals.GenerateAccounts();
try
{
//WORKS
MemberData account = accounts[accountNum];
account.balance += amount;
accounts[accountNum] = account;
//DOESN'T BUILD
//ref MemberData account = ref accounts[accountNum];
}
catch (KeyNotFoundException)
{
Console.WriteLine("Deposit failed, selected account number does not exist in accounts.txt");
}
}
}
static class Withdraw
{
}
static class CreateAccount
{
public static void Create(string name, long StartDeposit)
{
long accountNumber;
using (StreamReader sr = new StreamReader(Globals.accountIter))
{
accountNumber = Convert.ToInt64(sr.ReadLine());
sr.Close();
}
using (StreamWriter sr = new StreamWriter(Globals.accounts, true))
{
string accountAdd = $"{accountNumber}| {name}| {StartDeposit}";
sr.WriteLine(accountAdd);
Globals.SetNextAccountNumber();
Console.WriteLine("Successfully created account #" + accountNumber);
sr.Close();
}
}
}
struct MemberData
{
public long accountNum;
public string name;
public long balance;
public MemberData(long accountNum, string name, long balance)
{
this.accountNum = accountNum;
this.name = name;
this.balance = balance;
}
public override string ToString()
{
string thisStr = $"Account #: {accountNum}, Name: {name}, Balance: {balance}";
return thisStr;
}
}
static class Globals
{
public static string basepath = "C:\\Users\\root\\RiderProjects\\ConsoleApp1\\ATMProject\\";
public static string accounts = basepath + "accounts.txt"; //path to all accounts
public static string transactions = basepath + "transactions.txt"; //path to all accounts
public static string accountIter = basepath + "accountIter.txt"; //path to the current account iter
public static string transactIter = basepath + "transactIter.txt"; //path to the current account iter
//class values
public static long GetNextAccountNumber() //returns the next available account #
{
try
{
using (StreamReader sr = new StreamReader(accountIter))
{
long currAccountIter = Convert.ToInt64(sr.ReadLine());
sr.Close();
return currAccountIter;
}
}
catch (IOException e)
{
Console.WriteLine(e.Message);
return -1;
}
}
public static void SetNextAccountNumber() //increments the account #
{
long currAccountIter = GetNextAccountNumber();
if (currAccountIter == -1)
{
Console.WriteLine("Could not SetNextAccountNumber");
return;
}
using (StreamWriter sr = new StreamWriter(accountIter, false))
{
currAccountIter += 1;
sr.WriteLine(currAccountIter);
sr.Close();
}
}
public static Dictionary<long, MemberData> GenerateAccounts() //Returns Dictionary in form of <Account Number, MemberData>
{
Dictionary<long, MemberData> memberList = new Dictionary<long, MemberData>();
using (StreamReader sr = new StreamReader(accounts))
{
while (!sr.EndOfStream)
{
var memberSplit = sr.ReadLine().Split("| ", StringSplitOptions.RemoveEmptyEntries);
long accountNum = Convert.ToInt64(memberSplit[0]);
string name = memberSplit[1];
long balance = Convert.ToInt64(memberSplit[2]);
MemberData member = new MemberData(accountNum, name, balance);
memberList.Add(accountNum, member);
}
}
return memberList;
}
public static void DisplayAccounts()
{
Console.WriteLine("List of all Accounts:\n===========");
foreach (var member in GenerateAccounts())
{
Console.WriteLine(member.Value);
}
}
public static string GetTimestamp()
{
string timestamp = DateTime.Now.ToString("yyyyMMddhhmmss");
return timestamp;
}
private static string GetTransactionIter()
{
using (StreamReader sr = new StreamReader(transactIter))
{
string transactionID = sr.ReadLine();
sr.Close();
return transactionID;
}
}
public static string GenerateValidTransactionID()
{
string IDPart;
string timestamp = GetTimestamp();
string transactionID = GetTransactionIter();
string numberSlice = transactionID.Substring(14);
string timestampSlice = transactionID.Substring(0, 14);
if (timestamp == timestampSlice)
{
IDPart = Convert.ToString(Convert.ToInt64(numberSlice) + 1);
transactionID = timestamp + IDPart;
}
else
{
transactionID = timestamp + 0;
}
return transactionID;
}
public static void WriteTransactionID(string transactionID) //overwrite and write current transactionID to iter
{
using (StreamWriter sr = new StreamWriter(transactIter, false))
{
sr.WriteLine(transactionID);
sr.Close();
}
}
public static void GenereateAndWriteTransactionID()
{
WriteTransactionID(GenerateValidTransactionID());
}
}
}
Since C# 7 you can use local ref variables:
ref int niceName = ref localVariable.Values.SubArray.Here;
niceName += 20;
Note that you can not use this in async methods. And it does neither work with properties nor indexers (except for arrays).
Related
I am trying to write a c# program to make a Bank system with users creating bank accounts. And I want to be able to save these accounts given through the console to an excel file so it can retrieve them later if needed.
I followed a tutorial on how to do that and made minor adjustments to the code but when I try to run the program with some test accounts it writes them as blank accounts with no data and I can't see why it would do this.
public class Account
{
protected static int count = 1;
protected int accountNum;
protected string fName;
protected string lName;
protected int idNumber;
protected string address;
protected DateOnly birthday = new();
protected bool flag;
Savings.SpendingRef = Spendings(accountNum);
public Account(string fn, string ln, int idnum, string add, DateOnly bd)
{
//System.Threading.Interlocked.Increment(ref count);
this.accountNum = count++;
fName = fn;
lName = ln;
idNumber = idnum;
address = add;
birthday = bd;
flag = false;
}
public int AccountNum{get;}
public string FName{get; set;}
public string LName{get; set;}
public int IdNumber{get; set;}
public string Address{get; set;}
public DateOnly Birthday{get;}
public bool Flag{get; set;}
public string GetAccount(){
string s = "--------------------------------\n";
s += "Account Number :" + accountNum + "\nAccount belonging to :" + fName + " " + LName + "\nID Number :" + IdNumber + "\nAddress :" + address + "\nBirthday : " + birthday + "\nAccount blocked? :" + flag;
s += "\n--------------------------------\n";
Console.WriteLine(s);
return s;
}
}
Here is how the account class is implemented
static async Task Main(string[] args)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
var file = new FileInfo(#"..\..\..\SavedAccounts.XLSX");
//Other code not important
var Accounts = GetSetupData();
await SaveExcelFile(Accounts, file);
}
private static List<Account> GetSetupData()
{
List<Account> output = new()
{
new Account("Jerome", "Dupret", 42069, "68 rue des ecoles", new DateOnly(1999, 08, 10)),
new Account("Deborah", "Pierre", 69420, "68 rue des ecoles", new DateOnly(2000, 02, 18)),
};
return output;
}
Here are the test accounts I want to be inserted.
private static async Task SaveExcelFile(List<Account> accounts, FileInfo file)
{
DeleteIfExists(file);
using var package = new ExcelPackage(file);//basically a Dispose method to get rid of any useless data
var ws = package.Workbook.Worksheets.Add("AccountData");
var range = ws.Cells["A1"].LoadFromCollection(accounts, true);
range.AutoFitColumns();
await package.SaveAsync();
}
And this is where the call and the write function itself happen.
I used console Writes at every step to check if the accounts lose info at any time but they never do, I'm confused as to why it isn't sent properly.
It looks like you never set your public properties such as Fname, either you need to set them in the constructor or make the getter get the protected field.
for example
protected string fName;
public string FName
{
get
{
return fName;
}
set
{
fName = value;
}
}
This needs to be done on all the public fields so they all get the field that gets set in the constructor.
The set is not required but if you dont use it FName and fName can become diffrent values since fName is only set in the constructor.
I have this code which reads from my json file an array of words
public static string[] GetProfanity()
{
var json = string.Empty;
using (var fs = File.OpenRead("profanity.json"))
using (var sr = new StreamReader(fs, new UTF8Encoding(false)))
json = sr.ReadToEnd();
var profanityJson = JsonConvert.DeserializeObject<ProfanityJson>(json);
return profanityJson.badwords;
}
This is the json
{
"badwords" : ["bad", "stupid"]
}
And i try to access this here
public static bool ProfanityCheck(string inputString)
{
string[] badWords = GetProfanity();
string checkString = inputString.ToLower();
if (badWords.Any(checkString.Contains))
return true;
return false;
}
As requested I access the ProfanityCheck method here
[Command("echo")]
[Description("says whatever the user gives")]
public async Task Echo(CommandContext ctx, [RemainingText] string echoText)
{
bool hasProfanity = ProfanityFilter.ProfanityCheck(echoText);
if(hasProfanity)
{
var errMsg = ProfanityFilter.ErrorMessage();
var errSent = await ctx.Channel.SendMessageAsync(embed: errMsg).ConfigureAwait(false);
Thread.Sleep(3000);
await ctx.Channel.DeleteMessageAsync(errSent).ConfigureAwait(false);
await ctx.Channel.DeleteMessageAsync(ctx.Message).ConfigureAwait(false);
return;
}
await ctx.Channel.SendMessageAsync(echoText).ConfigureAwait(false);
}
and the struct I Deserialize it as
public struct ProfanityJson
{
[JsonProperty("badwords")]
public string[] badwords { get; private set; }
}
but when i attempt to search for this any bad words in a string I pass, nothing happens, no errors in the console, no output otherwise. I have it set up so that it sends me an error message when profanity is found, but in its current state it does nothing when profanity is passed
Your code seems to be correct... I would write the GetProfanity() in another way (and I wouldn't surely reread it every time a word is passed to to ProfanityCheck) but this is tangential to your problem. I've written a minimum testable example:
public class ProfanityJson
{
public string[] badwords { get; set; }
}
public static class ProfanityChecker
{
public static string[] GetProfanity()
{
var json = string.Empty;
using (var fs = File.OpenRead("profanity.json"))
using (var sr = new StreamReader(fs, new UTF8Encoding(false)))
json = sr.ReadToEnd();
var profanityJson = JsonConvert.DeserializeObject<ProfanityJson>(json);
return profanityJson.badwords;
}
public static string[] GetProfanity2()
{
using (var sr = new StreamReader("profanity.json"))
using (var jtr = new JsonTextReader(sr))
{
var ser = new JsonSerializer();
var profanityJson = ser.Deserialize<ProfanityJson>(jtr);
return profanityJson.badwords;
}
}
public static bool ProfanityCheck(string inputString)
{
string[] badWords = GetProfanity2();
Trace.WriteLine($"Loaded {badWords.Length} bad words");
string checkString = inputString.ToLower();
if (badWords.Any(checkString.Contains))
return true;
return false;
}
}
static void Main(string[] args)
{
Console.WriteLine(ProfanityChecker.ProfanityCheck("badder"));
}
So the only idea I have is that you are using a "stale" version of profanity.json. I've added a little loggin in the ProfanityCheck() method. It will go to the Output pane in Visual Studio.
(Would be a mess as a comment)
You could have your class like this:
public class ProfanityJson
{
[JsonProperty("badwords")]
public string[] Badwords { get; set; }
}
Is it like so? Json is case sensitive.
I'm trying to set value to different variables from messy text.
Main Dishes
name;group;price;TAX;number;Id;;Fullname;Description;
Bigger modifiers
Name;group;price;TAX;number;Id;/some/additional/stuff
Smaller modifiers
Name;;price;TAX;number;Id;/some/additional/stuff
Text
Omlet;Second Dishes;40,0000;;00027;326ef70c-8d29-4c63-94ce-0580f26f84ab;Omlet with chicken and mushroom sauce;;;;;
Onions;Vegetables;4,1000;21;00021;fe5bab77-72cf-474e-acbc-1562c2f6aa37;0/1/1/1/6;;;;
Tomatoes;Vegetables;4,2000;21;00022;180fa908-9428-444e-a1df-5b74a40def64;0/1/1/1/7;;;;
Day Soup;Soup;123,4560;9;10108;19674f89-a44a-423d-ae79-0fc020be8d72;;;;;;
Roast pork with sauce;Second Dishes;0,0500;21;1167;a929bf86-2b89-4af6-baf9-f37317e0d75f;;;;;;
Cucumbers;;0,5500;21;222;8e370b64-b1f8-4665-95ae-88327d877394;-/-/1/0/3;;;;;
Tomatoes with garlic;Vegetables;0,1100;21;00024;52d08882-41c2-4dc3-8c4b-998109b6aedc;-/-/1/0/3;;;;;
Salt;;0,3300;21;00025;39332fab-99e0-4663-a59a-fff0deab958d;-/-/1/0/3;;;;;
I have created a class
class Food
{
public string Name;
public string Group;
public string Price;
public string TaxPercent;
public string Number;
public string ID;
public string Type;
public string FullName;
public string Description;
}
Can someone explain me how i suppose to seperate part to smaller modifiers and bigger ones? Should I create new class or it is possible to work with one?
How should IF statement look like?
What i tried to so far
private void button1_Click(object sender, EventArgs e)
{
List<Food> List= new List<Food>();
using (StreamReader sr = new StreamReader(#"TEST.txt"))
{
while (!sr.EndOfStream)
{
string str;
string[] stringArray;
str = sr.ReadLine();
strArray = str.Split(';');
Food Dish = new Food();
Dish.Name = stringArray[0];
Dish.Group = stringArray[1];
Dish.Price = stringArray[2];
Dish.TaxPercent = stringArray[3];
List.Add(Dish);
textBox1.Text =displayMembers(List);
}
string displayMembers(List<Food> vegetables)
{
foreach (Food s in vegetables)
{
return s.ToString();
}
return null;
Out put
TESTREADER.FOOD //TESTREADER IS A FOLDER WHERE TEST FILE IS.
You can do something like this:
public List<Food> getFoods()
{
List<Food> foods = new List<Food>();
using (var fileReader = new StreamReader(#"file.txt"))
{
var line = fileReader.ReadLine();
while (line != null)
{
string[] data = s.Split(';');
line = fileReader.ReadLine();
Food food = new Food
{
Name = data[0];
Group = data[1];
(...)
}
line = fileReader.ReadLine();
}
return foods;
}
I am currently undertaking a project in which I am trying to make a RPG for a discord bot. I am currently struggling with how to implement a way to keep data for different servers separate. For example, I'm trying to store the location of the party for each server. I have tried testing moving from 'town' to 'forest'. It works on the server that the command is used, but all other servers that the bot is on also have their location updated to 'forest'. Since I'm new to c# as well I am struggling to work out a way to keep the location being updated on each server.
A possible solution would be to store an object for each guild in an array and reference it whenever guild specific data is required, however this doesn't seem like an elegant solution.
What would be the best way to achieve data separation between guilds?
MAIN
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestBot2.Modules;
namespace TestBot2
{
class Program
{
static void Main(string[] args){
new Program().RunBotAsync().GetAwaiter().GetResult();
}
private DiscordSocketClient _client;
private CommandService _command;
private IServiceProvider _service;
public async Task RunBotAsync()
{
_client = new DiscordSocketClient();
_command = new CommandService();
_service = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_command)
.BuildServiceProvider();
string botToken = *** BOT TOKEN ***;
//event subscription
_client.Log += Log;
await RegisterCommandAsync();
await _client.LoginAsync(TokenType.Bot, botToken);
await _client.StartAsync();
await Task.Delay(-1);
}
private Task Log(LogMessage arg)
{
Console.WriteLine(arg);
return null;
}
public async Task RegisterCommandAsync()
{
_client.MessageReceived += HandleCommandAsync;
await _command.AddModulesAsync(Assembly.GetEntryAssembly());
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
if (!(message is SocketUserMessage) || message.Author.IsBot) {
return;
}
int argPos = 1;
if (message.HasStringPrefix("cf!", ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos))
{
var context = new SocketCommandContext(_client, message);
var result = await _command.ExecuteAsync(context, argPos+1, _service);
if (!result.IsSuccess)
Console.WriteLine(result.ErrorReason);
}
}
}
}
UTILITY
static string location = "town"; //curent loc
static string[] locations = //array of vlaid loc
{
"town", "forest"
};
int[,] travelMtx = new int[,] //distance matrix
{
{0,2 },
{2,0 }
};
public string D6()
{
Random rnd = new Random();
string reply;
reply = Convert.ToString(rnd.Next(1, 7));
return reply;
}
public string charName(string charowner = "")
{
string charname;
System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection();
conn.ConnectionString = [FILE LOCATION]
conn.Open();
String my_query = "SELECT CharName FROM Chars WHERE CharOwner='" + charowner + "'";
Console.WriteLine(my_query);
OleDbCommand cmd = new OleDbCommand(my_query, conn);
charname = (string)cmd.ExecuteScalar();
Console.Write(charname);
return charname;
}
public string usermention(string user = "")
{
return user;
}
public string getLoc()
{
return Utility.location;
}
public void setLoc(string location)
{
Utility.location = location;
}
public bool checkLoc(string dest)
{
for (int i = 0; i < locations.Length; i++)
{
if (dest.ToLower() == locations[i])
{
return true;
}
}
return false;
}
public int travelTime(string location, string dest)
{
int x = 0;
int y = 0;
for (int i = 0; i < locations.Length; i++)
{
if (location.ToLower() == locations[i])
{
x = i;
}
if (dest.ToLower() == locations[i])
{
y= i;
}
}
return travelMtx[x,y];
}
}
}
TRAVEL
public class Travel : ModuleBase<SocketCommandContext>
{
[Command("Travel")]
public async Task PingAsync(string dest = "")
{
Utility Util = new Utility();
string loc = Util.getLoc();
int travelTime = 0;
if (Util.checkLoc(dest) == true)
{
travelTime = Util.travelTime(loc, dest);
}
Util.setLoc(dest);
await ReplyAsync("you are now in " + dest + " it took " + travelTime + " days" );
}
}
}
I think you could try to store it together with the serverip, so that you can ask for CharOwner='" + charowner + "'" and ServerIp='" + serverip + "'" in you WHERE part of the sql.
It's just a guess, but maybe it works. :)
I am relatively new to C# (WinForms), and had a question regarding combo boxes. I have a combo box of Reviewer objects (it is a custom class with an overridden ToString method) and am currently attempting to go through all the checked items and use them to generate a setup file.
Here is how the combo box is populated (populated on form load). Parameters is just a collection of linked lists and parsing code.
for (int i = 0; i < parameters.GetUsers().Count; i++)
{
UserList.Items.Add(parameters.GetUsersArray()[i], parameters.GetUsersArray()[i].isSelected());
}
Here is how I am trying to read it. setup is a StringBuilder. The problem is that GetID is not defined. Does the add function above cast the Reviewer object to a Object object? It looks a little funny since it creates a file fed into a Perl script. A sample desired output line looks like this: inspector0 => "chg0306",
for (int i = 0; i < UserList.CheckedItems.Count; i++)
{
setup.AppendLine("inspector" + i.ToString() + " => \t \"" +
UserList.CheckedItems[i].GetID() + "\",");
}
Here is the users class: (Sample User is ID = aaa0000 name: Bob Joe)
public class Reviewer
{
private string name;
private string id;
private bool selected;
public Reviewer(string newName, string newID, bool newSelected)
{
name = newName;
id = newID;
selected = newSelected;
}
public string GetName()
{
return name;
}
public override string ToString()
{
//string retVal = new string(' ', id.Length + name.Length + 1);
string retVal = id + '\t' + name;
return retVal;
}
public string GetID()
{
return id;
}
public bool isSelected()
{
return selected;
}
}
For posterity, here is the Parameters class:
public class ParameterLists
{
public ParameterLists()
{
projects = new LinkedList<string>();
reviewers = new LinkedList<Reviewer>();
}
public enum FileContents {
PROJECT_LIST,
USERS_LIST,
}
public LinkedList<Reviewer> GetUsers()
{
return reviewers;
}
public LinkedList<string> GetProjects()
{
return projects;
}
public Reviewer[] GetUsersArray()
{
Reviewer[] userArray = new Reviewer[reviewers.Count];
reviewers.CopyTo(userArray, 0);
return userArray;
}
public string[] GetProjectsArray()
{
String[] projectArray = new String[projects.Count];
projects.CopyTo(projectArray, 0);
return projectArray;
}
public void LoadParameters(string fileName)
{
//Reads the parameters from the input file.
}
private void CreateDefaultFile(string fileName)
{
// Create the file from the defaultfile , if it exists.
// Otherwise create a blank default file.
}
private LinkedList <string> projects;
private LinkedList <Reviewer> reviewers;
}
I am probably missing something simple, coming from embedded C++. Any help would be appreciated.
You have to cast that object:
((Reviewer)UserList.CheckedItems[i]).GetID()