I want to change my code, also I want to use Interfaces so if someone wants a future so it could be easily implemented, without changing too much code.
So I successfully implemented my interface IPalindromeChecker. But the problem is in MainWindow now. So I'm not sure but I would make another Interface and with public void Output(string text) method. I've tried adding a method in IPalindromeChecker public void Output(string text) but it didn't work.
interface ICheckPalindrome
{
bool IsPalindrome(string text);
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
int min = 0;
int max = text.Length - 1;
while (true)
{
if (min > max)
{
return true;
}
char a = text[min];
char b = text[max];
if (a != b)
{
return false;
}
min++;
max--;
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lblInput.Foreground = Brushes.ForestGreen;
lblResult.Foreground = Brushes.ForestGreen;
lblTitel.Foreground = Brushes.ForestGreen;
}
/// <summary>
/// User input and checking the input if the word a palindrome is.
/// </summary>
private void InputText_TextChanged(object sender, TextChangedEventArgs e)
{
string text = InputText.Text;
bool isPalindrome = TextChecker.PalindromeChecker(text); // HERE IS THE PROBLEM
OutputText.Text = text + (isPalindrome ? " is a palindrome" : " is NOT a palindrome");
if (InputText.Text == string.Empty)
{
OutputText.Clear();
}
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
int min = 0;
int max = text.Length - 1;
while (true)
{
if (min > max)
{
return true;
}
char a = text[min];
char b = text[max];
if (a != b)
{
return false;
}
min++;
max--;
}
}
}
It's unclear what TextChecker.PalindromeChecker is but if you want to be able to switch implementations of the ICheckPalindrome interface without having to modify your MainWindow, you should inject the window with an implementation of ICheckPalindrome at runtime and write your code against the interface.
You could for example use a property to do this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lblInput.Foreground = Brushes.ForestGreen;
lblResult.Foreground = Brushes.ForestGreen;
lblTitel.Foreground = Brushes.ForestGreen;
//use the PalindromeChecker as the default implementation
PalindromeChecker = new PalindromeChecker();
}
public ICheckPalindrome PalindromeChecker { get; set; } //<--
private void InputText_TextChanged(object sender, TextChangedEventArgs e)
{
string text = InputText.Text;
bool isPalindrome = PalindromeChecker.IsPalindrome(text);
OutputText.Text = text + (isPalindrome ? " is a palindrome" : " is NOT a palindrome");
if (InputText.Text == string.Empty)
{
OutputText.Clear();
}
}
}
Switching to another implementation is then as simple as just setting the PalindromeChecker property of the MainWindow to an instance of another class that implements the same ICheckPalindrome interface.
Related
I'm rewriting my project, so it's easier to edit in future, and want to implement interface.
I've implemented the interface, but it doesn't work in MainWindow, I'm not able to call the method.
So I've tried to use the PalindromeChecker as the default implementation
PalindromeChecker = new PalindromeChecker(); , so I can call the method but it didn't work.
interface ICheckPalindrome
{
bool IsPalindrome(string text);
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
......
//Code
}
}
}
namespace TextChecker
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lblInput.Foreground = Brushes.ForestGreen;
lblResult.Foreground = Brushes.ForestGreen;
lblTitel.Foreground = Brushes.ForestGreen;
}
/// <summary>
/// User input and checking the input if the word a palindrome is.
/// </summary>
private void InputText_TextChanged(object sender, TextChangedEventArgs e)
{
string text = InputText.Text;
bool isPalindrome = PalindromeChecker.IsPalindrome(text);
OutputText.Text = text + (isPalindrome ? " is a palindrome" : " is NOT a palindrome");
if (InputText.Text == string.Empty)
{
OutputText.Clear();
}
}
private void ButtonClicked(object sender, RoutedEventArgs e)
{
SubWindow subWindow = new SubWindow();
subWindow.Show();
}
}
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
int min = 0;
int max = text.Length - 1;
while (true)
{
if (min > max)
{
return true;
}
char a = text[min];
char b = text[max];
if (a != b)
{
return false;
}
min++;
max--;
}
}
}
I'm really stuck here, I would like to thank you in advance.
As far as can see, you don't want interfaces, class instances to check for a palindrome (do you really want to implement several algorithms to choose from?), but a static method:
// Let's class be partial one: if you want to add a method it it
// you don't have to modify this code but
// add a chunk public partial static class MyStringRoutine {...}
public partial static class MyStringRoutine {
public static bool IsPalindrome(string text) {
//DONE: do not forget about special cases
if (string.IsNullOrEmpty(text))
return true;
for (int i = 0; i < text.Length / 2; ++i)
if (text[i] != text[text.Length - 1 - i])
return false;
return true;
}
}
Then you can use it:
private void InputText_TextChanged(object sender, TextChangedEventArgs e) {
if (string.IsNullOrEmpty(InputText.Text))
OutputText.Clear();
else {
string suffix = MyStringRoutine.IsPalindrome(InputText.Text)
? "is a palindrome"
: "is NOT a palindrome";
OutputText.Text = $"{InputText.Text} {suffix}";
}
}
If you have to implement ICheckPalindrome interface, and thus to work with class instance, you have to create the instance:
private void InputText_TextChanged(object sender, TextChangedEventArgs e) {
if (string.IsNullOrEmpty(InputText.Text))
OutputText.Clear();
else {
// You have to create the instance (checker)
ICheckPalindrome checker = new PalindromeChecker();
// IsPalindrome is the instance method; you can't call is as
// PalindromeChecker.IsPalindrome
string suffix = checker.IsPalindrome(InputText.Text)
? "is a palindrome"
: "is NOT a palindrome";
OutputText.Text = $"{InputText.Text} {suffix}";
}
}
From code you've posted it'd seem that interface ICheckPalindrome is less accessible than PalindromeChecker. Not sure how and why your compiler would let that slip, but you should try changing your code to
public interface ICheckPalindrome
{
bool IsPalindrome(string text);
}
So the interface will reflect its implementation (or rather other way around).
I am working on my own multithreading for my algorithm independed pathfinding for unity. However, when I am executing two the same class I get a memory leak and when only executing one instance I am having no issues. I really want to use at least two threads if it is necessary.
Below is the class I have issues with. Keep in mind, that two independend threads will have to execute parts of this script. AddJob can be called from the main unity thread but will most likely be called from another update thread for the agents.
namespace Plugins.PathFinding.Threading
{
internal class PathFindingThread
{
private Thread m_Worker;
private volatile Queue<CompletedProcessingCallback> m_CallbackQueue;
private volatile Queue<IAlgorithm> m_QueuedTasks;
internal int GetTaskCount
{
get
{
return m_QueuedTasks.Count;
}
}
internal PathFindingThread()
{
m_Worker = new Thread(Run);
m_CallbackQueue = new Queue<CompletedProcessingCallback>();
m_QueuedTasks = new Queue<IAlgorithm>();
}
private void Run()
{
Debug.Log("<b><color=green> [ThreadInfo]:</color></b> PathFinding Thread Started ");
try
{
while(true)
{
if (m_QueuedTasks.Count > 0)
{
IAlgorithm RunningTask = m_QueuedTasks.Dequeue();
RunningTask.FindPath(new IAlgorithmCompleted(AddCallback));
}
else
break;
}
Debug.Log("<b><color=red> [ThreadInfo]:</color></b> PathFinding Worker is idle and has been Stopped");
}
catch(Exception)
{
Debug.Log("<b><color=red> [ThreadInfo]:</color></b> PathFinding thread encountred an error and has been aborted");
}
}
internal void AddJob(IAlgorithm AlgorithmToRun)
{
m_QueuedTasks.Enqueue(AlgorithmToRun);
//Debug.Log("Added Job To Queue");
}
private void AddCallback(CompletedProcessingCallback callback)
{
m_CallbackQueue.Enqueue(callback);
}
private void Update()
{
if (m_CallbackQueue.Count > 0)
{
if (m_CallbackQueue.Peek().m_Callback != null) { }
m_CallbackQueue.Peek().m_Callback.Invoke(m_CallbackQueue.Peek().m_Path);
m_CallbackQueue.Dequeue();
}
if (m_Worker.ThreadState != ThreadState.Running && m_QueuedTasks.Count != 0)
{
m_Worker = new Thread(Run);
m_Worker.Start();
}
}
}
internal delegate void IAlgorithmCompleted(CompletedProcessingCallback callback);
internal struct CompletedProcessingCallback
{
internal volatile FindPathCompleteCallback m_Callback;
internal volatile List<GridNode> m_Path;
}
}
namespace Plugins.PathFinding
{
internal enum TypeOfNode
{
Ground,
Air
}
//used to store location information since array can only take rounded numbers
internal struct Position
{
internal int x;
internal int y;
internal int z;
}
internal class GridNode
{
internal Position M_PostitionInGrid { get; private set; }
internal Vector3 M_PostitionInWorld { get; private set; }
internal TypeOfNode M_type { get; private set; }
internal bool m_IsWalkable = true;
internal GridNode m_ParrentNode;
internal int Hcost;
internal int Gcost;
internal int Fcost { get { return Hcost + Gcost; } }
internal GridNode(Position postion , Vector3 WorldPosition)
{
M_PostitionInGrid = postion;
m_IsWalkable = true;
M_PostitionInWorld = WorldPosition;
}
}
}
internal delegate void FindPathCompleteCallback(List<GridNode> Path);
internal abstract class IAlgorithm
{
protected GridNode m_SavedStart;
protected GridNode m_SavedTarget;
protected List<GridNode> m_LocatedPath;
protected FindPathCompleteCallback m_Callback;
internal FindPathCompleteCallback GetCallback
{
get
{
return m_Callback;
}
}
protected PathFindingGrid m_grid;
internal abstract void FindPath(IAlgorithmCompleted callback);
protected abstract List<GridNode> CreatePath(PathFindingGrid Grid, GridNode Start, GridNode Target);
protected abstract List<GridNode> RetracePath(GridNode start, GridNode target);
}
namespace Plugins.PathFinding.Astar
{
internal class AstarFinder : IAlgorithm
{
//construction of the Algorithm
internal AstarFinder(GridNode start, GridNode target, FindPathCompleteCallback Callback)
{
m_SavedStart = start;
m_SavedTarget = target;
m_Callback = Callback;
m_LocatedPath = new List<GridNode>();
m_grid = PathFindingGrid.GetInstance;
}
//function to start finding a path
internal override void FindPath(IAlgorithmCompleted callback)
{
//running Algorithm and getting the path
m_LocatedPath = CreatePath(PathFindingGrid.GetInstance, m_SavedStart, m_SavedTarget);
callback.Invoke(
new CompletedProcessingCallback()
{
m_Callback = m_Callback,
m_Path = m_LocatedPath
});
}
//Algorithm
protected override List<GridNode> CreatePath(PathFindingGrid Grid, GridNode Start, GridNode Target)
{
if(Grid == null ||
Start == null ||
Target == null)
{
UnityEngine.Debug.Log("Missing Parameter, might be outside of grid");
return new List<GridNode>();
}
List<GridNode> Path = new List<GridNode>();
List<GridNode> OpenSet = new List<GridNode>();
List<GridNode> ClosedSet = new List<GridNode>();
OpenSet.Add(Start);
int Retry = 0;
while (OpenSet.Count > 0)
{
if(Retry > 3000 || Grid == null)
{
UnityEngine.Debug.Log("Path Inpossible Exiting");
break;
}
GridNode CurrentNode = OpenSet[0];
for (int i = 0; i < OpenSet.Count; i++)
{
if(OpenSet[i].Fcost < CurrentNode.Fcost || OpenSet[i].Fcost == CurrentNode.Fcost && OpenSet[i].Hcost < CurrentNode.Hcost)
{
CurrentNode = OpenSet[i];
}
}
OpenSet.Remove(CurrentNode);
ClosedSet.Add(CurrentNode);
if(CurrentNode == Target)
{
Path = RetracePath(CurrentNode,Start);
break;
}
GridNode[] neighbour = Grid.GetNeighbouringNodes(CurrentNode);
for (int i = 0; i < neighbour.Length; i++)
{
if (!neighbour[i].m_IsWalkable || ClosedSet.Contains(neighbour[i]))
continue;
int CostToNeighbour = CurrentNode.Gcost + Grid.GetDistance(CurrentNode, neighbour[i]);
if(CostToNeighbour < neighbour[i].Gcost || !OpenSet.Contains(neighbour[i]))
{
neighbour[i].Gcost = CostToNeighbour;
neighbour[i].Hcost = Grid.GetDistance(neighbour[i], Target);
neighbour[i].m_ParrentNode = CurrentNode;
if (!OpenSet.Contains(neighbour[i]))
OpenSet.Add(neighbour[i]);
}
}
Retry++;
}
return Path;
}
//retracing the path out of a node map
protected override List<GridNode> RetracePath(GridNode start, GridNode target)
{
List<GridNode> Output = new List<GridNode>();
GridNode current = start;
while(current != target)
{
Output.Add(current);
current = current.m_ParrentNode;
}
Output.Reverse();
return Output;
}
}
}
This shows the core of your code made thread safe.
internal class PathFindingThread
{
Task m_Worker;
ConcurrentQueue<CompletedProcessingCallback> m_CallbackQueue;
ConcurrentQueue<IAlgorithm> m_QueuedTasks;
internal int GetTaskCount
{
get
{
return m_QueuedTasks.Count;
}
}
internal PathFindingThread()
{
m_CallbackQueue = new ConcurrentQueue<CompletedProcessingCallback>();
m_QueuedTasks = new ConcurrentQueue<IAlgorithm>();
m_Worker = Task.Factory.StartNew(() =>
{
while (true)
{
IAlgorithm head = null;
if (m_QueuedTasks.TryDequeue(out head))
{
head.FindPath(new IAlgorithmCompleted(AddCallback));
}
else
{
Task.Delay(0);
}
}
});
}
internal void AddJob(IAlgorithm AlgorithmToRun)
{
m_QueuedTasks.Enqueue(AlgorithmToRun);
}
private void AddCallback(CompletedProcessingCallback callback)
{
m_CallbackQueue.Enqueue(callback);
}
private void Update()
{
CompletedProcessingCallback cb = null;
if (m_CallbackQueue.TryDequeue(out cb))
{
cb.m_Callback.Invoke(cb.m_Path);
}
}
}
Volatile is only good for changing the value of the field - not calling methods on a collection that is referenced by the field.
You propably do not need to have Volatile in CompletedProcessingCallback, but it depends where else this is used. Certainly having volatile on a struct field is a bad smell.
Resolve these thread issues first, then see if you still have the problem.
Basically I want to launch an event when a string reaches a specific length.
I have a Static String
Static String _Info;
So i have My Delegate that has an integer as a Parameter !
public Delegate void ReachLengthHandler(int Length);
and My event :
public event ReachLengthHandler ReachLengthEvent;
And a Method that Keep addingSome informations to that string :
public void AddInfo()
{
new Thread(() =>
{
while(true)
_Info += ""; //Basically add the inputs of the user here !
if (_Info.Length > 500)
{
if (ReachLengthEvent != null)
ReachLengthEvent(_Info.Length);
}
}).Start();
}
Do you think its the right way to do this event or there are any cleaner ways ?
EDIT :
I want this event because I want to save this string in a Database table row so I don't want to expand the possible size of a row !
As some pointed out in the comments, you may be trying to solve an instance of the XY Problem -- but assuming you're not, you are not approaching things in an object-oriented way, starting with encapsulation.
This could be a start, FWIW:
public class MaxLengthEventArgs : EventArgs
{
public MaxLengthEventArgs(string value)
{
LastAppended = value;
}
public string LastAppended { get; private set; }
}
public delegate void MaxLengthEventHandler(object sender, MaxLengthEventArgs args);
public class StringAccumulator
{
protected StringBuilder Builder { get; private set; }
public StringAccumulator(int maxLength)
{
if (maxLength < 0)
{
throw new ArgumentOutOfRangeException("maxLength", "must be positive");
}
Builder = new StringBuilder();
MaxLength = maxLength;
}
public StringAccumulator Append(string value)
{
if (!string.IsNullOrEmpty(value))
{
var sofar = value.Length + Builder.Length;
if (sofar <= MaxLength)
{
Builder.Append(value);
if ((OnMaxLength != null) && (sofar == MaxLength))
{
OnMaxLength(this, new MaxLengthEventArgs(value));
}
}
else
{
throw new InvalidOperationException("overflow");
}
}
return this;
}
public override string ToString()
{
return Builder.ToString();
}
public int MaxLength { get; private set; }
public event MaxLengthEventHandler OnMaxLength;
}
class Program
{
static void Test(object sender, MaxLengthEventArgs args)
{
var acc = (StringAccumulator)sender;
Console.WriteLine(#"max length ({0}) reached with ""{1}"" : ""{2}""", acc.MaxLength, args.LastAppended, acc.ToString());
}
public static void Main(string[] args)
{
var acc = new StringAccumulator(10);
try
{
acc.OnMaxLength += Test;
acc.Append("abc");
acc.Append("def");
acc.Append("ghij");
Console.WriteLine();
acc.Append("ouch...");
Console.WriteLine("(I won't show)");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
}
Also, keep in mind that strings in .NET are immutable.
Accumulating them using string concatenation, as you did in
_Info += ""
... isn't going to scale well (performance-wise).
'HTH,
Usually eventhandler is used with specific signature.
public delegate void ReachLengthHandler(object sender, EventArgs args);
class Program
{
public event ReachLengthHandler handler;
private const int Threshhold = 500;
public string Info
{
set
{
if (value.Length > Threshhold)
{
this.OnReachLength(null);
}
}
}
public void OnReachLength(EventArgs args)
{
this.handler?.Invoke(this, args);
}
}
I'm trying to make a command line for my game in Unity and when adding system information commands like memory I encountered this problem. I hope the community can help me. Thanks in advance. The errors occur at lines 216, 223, and 225.
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Text;
public delegate void CommandHandler(string[] args);
public class ConsoleController {
#region Event declarations
// Used to communicate with ConsoleView
public delegate void LogChangedHandler(string[] log);
public event LogChangedHandler logChanged;
public delegate void VisibilityChangedHandler(bool visible);
public event VisibilityChangedHandler visibilityChanged;
#endregion
/// <summary>
/// Object to hold information about each command
/// </summary>
class CommandRegistration {
public string command { get; private set; }
public CommandHandler handler { get; private set; }
public string help { get; private set; }
public CommandRegistration(string command, CommandHandler handler, string help) {
this.command = command;
this.handler = handler;
this.help = help;
}
}
/// <summary>
/// How many log lines should be retained?
/// Note that strings submitted to appendLogLine with embedded newlines will be counted as a single line.
/// </summary>
const int scrollbackSize = 20;
Queue<string> scrollback = new Queue<string>(scrollbackSize);
List<string> commandHistory = new List<string>();
Dictionary<string, CommandRegistration> commands = new Dictionary<string, CommandRegistration>();
public string[] log { get; private set; } //Copy of scrollback as an array for easier use by ConsoleView
const string repeatCmdName = "!!"; //Name of the repeat command, constant since it needs to skip these if they are in the command history
public ConsoleController() {
//When adding commands, you must add a call below to registerCommand() with its name, implementation method, and help text.
registerCommand("babble", babble, "Example command that demonstrates how to parse arguments. babble [word] [# of times to repeat]");
registerCommand("echo", echo, "echoes arguments back as array (for testing argument parser)");
registerCommand("help", help, "Print this help.");
registerCommand("hide", hide, "Hide the console.");
registerCommand(repeatCmdName, repeatCommand, "Repeat last command.");
registerCommand("reload", reload, "Reload game.");
registerCommand("resetprefs", resetPrefs, "Reset & saves PlayerPrefs.");
registerCommand("ver", ver, "Displays the current game version.");
registerCommand("buildver", buildver, "Displays the current build.");
registerCommand("sys", sys, "Displays basic system information.");
registerCommand("devinfo", devinfo, "Displays important developer information.");
}
void registerCommand(string command, CommandHandler handler, string help) {
commands.Add(command, new CommandRegistration(command, handler, help));
}
public void appendLogLine(string line) {
Debug.Log(line);
if (scrollback.Count >= ConsoleController.scrollbackSize) {
scrollback.Dequeue();
}
scrollback.Enqueue(line);
log = scrollback.ToArray();
if (logChanged != null) {
logChanged(log);
}
}
public void runCommandString(string commandString) {
appendLogLine("$ " + commandString);
string[] commandSplit = parseArguments(commandString);
string[] args = new string[0];
if (commandSplit.Length < 1) {
appendLogLine(string.Format("Unable to process command '{0}'", commandString));
return;
} else if (commandSplit.Length >= 2) {
int numArgs = commandSplit.Length - 1;
args = new string[numArgs];
Array.Copy(commandSplit, 1, args, 0, numArgs);
}
runCommand(commandSplit[0].ToLower(), args);
commandHistory.Add(commandString);
}
public void runCommand(string command, string[] args) {
CommandRegistration reg = null;
if (!commands.TryGetValue(command, out reg)) {
appendLogLine(string.Format("Unknown command '{0}', type 'help' for list.", command));
} else {
if (reg.handler == null) {
appendLogLine(string.Format("Unable to process command '{0}', handler was null.", command));
} else {
reg.handler(args);
}
}
}
static string[] parseArguments(string commandString)
{
LinkedList<char> parmChars = new LinkedList<char>(commandString.ToCharArray());
bool inQuote = false;
var node = parmChars.First;
while (node != null)
{
var next = node.Next;
if (node.Value == '"') {
inQuote = !inQuote;
parmChars.Remove(node);
}
if (!inQuote && node.Value == ' ') {
node.Value = '\n';
}
node = next;
}
char[] parmCharsArr = new char[parmChars.Count];
parmChars.CopyTo(parmCharsArr, 0);
return (new string(parmCharsArr)).Split(new char[] {'\n'} , StringSplitOptions.RemoveEmptyEntries);
}
#region Command handlers
//Implement new commands in this region of the file.
/// <summary>
/// A test command to demonstrate argument checking/parsing.
/// Will repeat the given word a specified number of times.
/// </summary>
void babble(string[] args) {
if (args.Length < 2) {
appendLogLine("Expected 2 arguments.");
return;
}
string text = args[0];
if (string.IsNullOrEmpty(text)) {
appendLogLine("Expected arg1 to be text.");
} else {
int repeat = 0;
if (!Int32.TryParse(args[1], out repeat)) {
appendLogLine("Expected an integer for arg2.");
} else {
for(int i = 0; i < repeat; ++i) {
appendLogLine(string.Format("{0} {1}", text, i));
}
}
}
}
void echo(string[] args) {
StringBuilder sb = new StringBuilder();
foreach (string arg in args)
{
sb.AppendFormat("{0},", arg);
}
sb.Remove(sb.Length - 1, 1);
appendLogLine(sb.ToString());
}
void help(string[] args) {
foreach(CommandRegistration reg in commands.Values) {
appendLogLine(string.Format("{0}: {1}", reg.command, reg.help));
}
}
void hide(string[] args) {
if (visibilityChanged != null) {
visibilityChanged(false);
}
}
void repeatCommand(string[] args) {
for (int cmdIdx = commandHistory.Count - 1; cmdIdx >= 0; --cmdIdx) {
string cmd = commandHistory[cmdIdx];
if (String.Equals(repeatCmdName, cmd)) {
continue;
}
runCommandString(cmd);
break;
}
}
void reload(string[] args) {
Application.LoadLevel(Application.loadedLevel);
}
void resetPrefs(string[] args) {
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
}
void ver(string[] args) {
appendLogLine("La Llorona 16w14~");
}
void buildver(string[] args)
{
appendLogLine("Build 040916.04");
}
void sys(string[] args)
{
appendLogLine(SystemInfo.operatingSystem);
appendLogLine(SystemInfo.processorType);
appendLogLine(SystemInfo.systemMemorySize);
appendLogLine(SystemInfo.graphicsDeviceName);
}
void devinfo(string[] args)
{
appendLogLine(SystemInfo.deviceModel);
appendLogLine(SystemInfo.deviceType);
appendLogLine(SystemInfo.graphicsDeviceName);
appendLogLine(SystemInfo.graphicsMemorySize);
}
#endregion
}
appendLogLine takes string as the parameter but you are passing int to it with appendLogLine(SystemInfo.systemMemorySize);, appendLogLine(SystemInfo.deviceType); and appendLogLine(SystemInfo.graphicsMemorySize);
You have to convert those integers to strings with ToString(). So those lines should be
appendLogLine(SystemInfo.systemMemorySize.ToString());
appendLogLine(SystemInfo.deviceType.ToString());
appendLogLine(SystemInfo.graphicsMemorySize.ToString());
Also, Application.LoadLevel(Application.loadedLevel); is deprecated so include using UnityEngine.SceneManagement; at the top then change Application.LoadLevel to
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
The fixed version of your code:
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine.SceneManagement;
public delegate void CommandHandler(string[] args);
public class ConsoleController
{
#region Event declarations
// Used to communicate with ConsoleView
public delegate void LogChangedHandler(string[] log);
public event LogChangedHandler logChanged;
public delegate void VisibilityChangedHandler(bool visible);
public event VisibilityChangedHandler visibilityChanged;
#endregion
/// <summary>
/// Object to hold information about each command
/// </summary>
class CommandRegistration
{
public string command { get; private set; }
public CommandHandler handler { get; private set; }
public string help { get; private set; }
public CommandRegistration(string command, CommandHandler handler, string help)
{
this.command = command;
this.handler = handler;
this.help = help;
}
}
/// <summary>
/// How many log lines should be retained?
/// Note that strings submitted to appendLogLine with embedded newlines will be counted as a single line.
/// </summary>
const int scrollbackSize = 20;
Queue<string> scrollback = new Queue<string>(scrollbackSize);
List<string> commandHistory = new List<string>();
Dictionary<string, CommandRegistration> commands = new Dictionary<string, CommandRegistration>();
public string[] log { get; private set; } //Copy of scrollback as an array for easier use by ConsoleView
const string repeatCmdName = "!!"; //Name of the repeat command, constant since it needs to skip these if they are in the command history
public ConsoleController()
{
//When adding commands, you must add a call below to registerCommand() with its name, implementation method, and help text.
registerCommand("babble", babble, "Example command that demonstrates how to parse arguments. babble [word] [# of times to repeat]");
registerCommand("echo", echo, "echoes arguments back as array (for testing argument parser)");
registerCommand("help", help, "Print this help.");
registerCommand("hide", hide, "Hide the console.");
registerCommand(repeatCmdName, repeatCommand, "Repeat last command.");
registerCommand("reload", reload, "Reload game.");
registerCommand("resetprefs", resetPrefs, "Reset & saves PlayerPrefs.");
registerCommand("ver", ver, "Displays the current game version.");
registerCommand("buildver", buildver, "Displays the current build.");
registerCommand("sys", sys, "Displays basic system information.");
registerCommand("devinfo", devinfo, "Displays important developer information.");
}
void registerCommand(string command, CommandHandler handler, string help)
{
commands.Add(command, new CommandRegistration(command, handler, help));
}
public void appendLogLine(string line)
{
Debug.Log(line);
if (scrollback.Count >= ConsoleController.scrollbackSize)
{
scrollback.Dequeue();
}
scrollback.Enqueue(line);
log = scrollback.ToArray();
if (logChanged != null)
{
logChanged(log);
}
}
public void runCommandString(string commandString)
{
appendLogLine("$ " + commandString);
string[] commandSplit = parseArguments(commandString);
string[] args = new string[0];
if (commandSplit.Length < 1)
{
appendLogLine(string.Format("Unable to process command '{0}'", commandString));
return;
}
else if (commandSplit.Length >= 2)
{
int numArgs = commandSplit.Length - 1;
args = new string[numArgs];
Array.Copy(commandSplit, 1, args, 0, numArgs);
}
runCommand(commandSplit[0].ToLower(), args);
commandHistory.Add(commandString);
}
public void runCommand(string command, string[] args)
{
CommandRegistration reg = null;
if (!commands.TryGetValue(command, out reg))
{
appendLogLine(string.Format("Unknown command '{0}', type 'help' for list.", command));
}
else
{
if (reg.handler == null)
{
appendLogLine(string.Format("Unable to process command '{0}', handler was null.", command));
}
else
{
reg.handler(args);
}
}
}
static string[] parseArguments(string commandString)
{
LinkedList<char> parmChars = new LinkedList<char>(commandString.ToCharArray());
bool inQuote = false;
var node = parmChars.First;
while (node != null)
{
var next = node.Next;
if (node.Value == '"')
{
inQuote = !inQuote;
parmChars.Remove(node);
}
if (!inQuote && node.Value == ' ')
{
node.Value = '\n';
}
node = next;
}
char[] parmCharsArr = new char[parmChars.Count];
parmChars.CopyTo(parmCharsArr, 0);
return (new string(parmCharsArr)).Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
}
#region Command handlers
//Implement new commands in this region of the file.
/// <summary>
/// A test command to demonstrate argument checking/parsing.
/// Will repeat the given word a specified number of times.
/// </summary>
void babble(string[] args)
{
if (args.Length < 2)
{
appendLogLine("Expected 2 arguments.");
return;
}
string text = args[0];
if (string.IsNullOrEmpty(text))
{
appendLogLine("Expected arg1 to be text.");
}
else
{
int repeat = 0;
if (!Int32.TryParse(args[1], out repeat))
{
appendLogLine("Expected an integer for arg2.");
}
else
{
for (int i = 0; i < repeat; ++i)
{
appendLogLine(string.Format("{0} {1}", text, i));
}
}
}
}
void echo(string[] args)
{
StringBuilder sb = new StringBuilder();
foreach (string arg in args)
{
sb.AppendFormat("{0},", arg);
}
sb.Remove(sb.Length - 1, 1);
appendLogLine(sb.ToString());
}
void help(string[] args)
{
foreach (CommandRegistration reg in commands.Values)
{
appendLogLine(string.Format("{0}: {1}", reg.command, reg.help));
}
}
void hide(string[] args)
{
if (visibilityChanged != null)
{
visibilityChanged(false);
}
}
void repeatCommand(string[] args)
{
for (int cmdIdx = commandHistory.Count - 1; cmdIdx >= 0; --cmdIdx)
{
string cmd = commandHistory[cmdIdx];
if (String.Equals(repeatCmdName, cmd))
{
continue;
}
runCommandString(cmd);
break;
}
}
void reload(string[] args)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
void resetPrefs(string[] args)
{
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
}
void ver(string[] args)
{
appendLogLine("La Llorona 16w14~");
}
void buildver(string[] args)
{
appendLogLine("Build 040916.04");
}
void sys(string[] args)
{
appendLogLine(SystemInfo.operatingSystem);
appendLogLine(SystemInfo.processorType);
appendLogLine(SystemInfo.systemMemorySize.ToString());
appendLogLine(SystemInfo.graphicsDeviceName);
}
void devinfo(string[] args)
{
appendLogLine(SystemInfo.deviceModel);
appendLogLine(SystemInfo.deviceType.ToString());
appendLogLine(SystemInfo.graphicsDeviceName);
appendLogLine(SystemInfo.graphicsMemorySize.ToString());
}
#endregion
}
I have a winform I'm working on that needs change a label when it hits a specific phase and the same label when it's complete. I have an interface setup and a class that implements this interface. In the class constructor, I'm trying to add the Action<> as a parameter but I am having trouble processing what I need to do in order to get it to flow correctly.
Never could get it to work :/
While your example i cannot give specific example because it confuses me. I can still answer some questions.
Does this help?
private Action _methodOnProgress;
public ChipLoadFirmware(Action method)
{
_methodOnProgress = method;
}
public bool LoadFirmWare()
{
(p) = >
{
_methodOnProgress();
}
}
Maybe an event pattern here will be more reliable.
First add a
public event EventHandler Progress
to your ChipLoadFirmware class, and make it public in the manager class (lets assume it's called Manager and the ChiLoad instance is called Loader)
So, when you instance the manager in the form you add
Manager.Loader.Progress += (o, e) => { /* Change the label */ };
And finally in the (p) => { } do
Progress(this, EventArgs.Empty);
EDIT Here is your code with the changes, supose the class Form is your form and labelToChange the label which must be changed;
/// <summary>
/// Represents an object that can load the firmware
/// </summary>
public interface ILoadFirmware
{
/// <summary>
/// Loads firmware on a device
/// </summary>
bool LoadFirmware();
event EventHandler Progress;
event EventHandler Status;
}
public class ChipLoadFirmware : ILoadFirmware
{
private readonly log4net.ILog logger = Logging.Log4NetManager.GetLogger();
private readonly ImageLoader imageLoader = new ImageLoader ();
private bool abort = false;
private string cmdText = string.Empty;
private string errortext = string.Empty;
private string isaIp;
public event EventHandler Progress;
public event EventHandler Status;
/// <summary>
/// Sets up an ImageLoader
/// </summary>
/// <param name="isaTargetIpAddress">The IP address of the ISA to load the firmware on</param>
public ChipLoadFirmware (string isaTargetIpAddress)
{
this.isaIp = isaTargetIpAddress;
imageLoader.EventHandler += (s, e) => { };
logger.DebugFormat("Using IP Address {0}", isaTargetIpAddress);
}
/// <summary>
/// Loads the firmware onto the device
/// </summary>
/// <returns>Returns true if successful</returns>
public bool LoadFirmware()
{
bool result = imageLoader.Run(this.isaIp,
false,
Command.Command,
string.Empty,
CommandFlag.CommandFlag,
2000,
(p) => { Progress(this, EventArgs.Empty); },
(s) => { Status(this, EventArgs.Empty); },
ref this.abort,
out this.cmdText,
out this.errortext);
if (!result)
{
result = false;
throw new InvalidOperationException(
string.Format(
"ImageLoader failed with message: {0}",
errortext));
}
return result;
}
}
public class CalibrationManager : ICalibrationManager
{
const uint startAddress = 0x00000000;
const uint startAddress = 0x00000000;
private readonly log4net.ILog logger;
private readonly ISignalGenerator sigGenerator;
public readonly ILoadFirmware loadFirmware;
private readonly IReader reader;
private readonly IValueParser valueParser;
private readonly IRepository<CalibrationValue> calibrationRepository;
private readonly IRepository<UIDList> uidRepository;
private string uid;
public CalibrationManager(ISignalGenerator sigGen, ILoadFirmware loadFirmware, IReader chipRreader, IValueParser chipValueParser, IRepository<UIDList> uidRepo, IRepository<CalibrationValue> calRepo)
{
if (sigGen == null ||
loadFirmware == null ||
chipReader == null ||
chipValueParser == null ||
calRepo == null ||
uidRepo == null) throw new ArgumentNullException();
logger = Logging.Log4NetManager.GetLogger();
this.sigGenerator = sigGen;
this.loadFirmware = loadFirmware;
this.reader = chipReader;
this.valueParser = chipValueParser;
this.uidRepository = uidRepo;
this.calibrationRepository = calRepo;
}
public void Calibrate(bool reuse)
{
int voltageG;
int currentG;
// Read uid from device
this.uid = uidReader.ReadUid();
logger.DebugFormat("Read UID {0}", this.uid);
var possibleCalibrations = getCalibrationDataFromRepo();
if (possibleCalibrations.Any() && reuse)
{
logger.Debug("Reusing existing values");
var existingCalibration = possibleCalibrations.OrderByDescending(c => c.DateCalibrated).First();
currentG = existingCalibration.CurrentGain;
voltageG = existingCalibration.VoltageGain;
}
else
{
logger.Debug("Reading new values");
// Set voltage and current for signal generator
sigGenerator.SetOutput(187, 60);
// Load firmware onto chip for CLI commands
loadFirmware.LoadFirmware();
UpdateStateChange(StateOfDevice.LoadFirmware);
// Read voltage from device and calibrate
voltageG = valueParser.ReadVoltageValue();
UpdateStateChange(StateOfDevice.CalibrateVoltage);
// Read current from device and calibrate
sigGenerator.SetOutput(38, 60);
currentG = valueParser.ReadCurrentValue();
UpdateStateChange(StateOfDevice.CalibrateCurrent);
// Insert values into table
CalibrationValue cv = new CalibrationValue();
cv.UidId = uidRepository.GetBy(e => e.UID.Equals(this.uid)).Id;
cv.CurrentGain = currentG;
cv.VoltageGain = voltageG;
calibrationRepository.Insert(cv);
calibrationRepository.Submit();
UpdateStateChange(StateOfDevice.DatabaseInsert);
}
logger.DebugFormat("Updated database for current gain to {0} and voltage gain to {1}", currentG, voltageG);
// Once here, device has passed all phases and device is calibrated
UpdateStateChange(StateOfDevice.CalibrationComplete);
}
private IQueryable<CalibrationValue> getCalibrationDataFromRepo()
{
int uidId = uidRepository.GetBy(e => e.UID.Equals(this.uid)).Id;
return calibrationRepository.SearchFor(c => c.UidId == uidId);
}
public bool CalibrationDataExists()
{
this.uid = uidReader.ReadUid();
logger.DebugFormat("Read UID {0}", this.uid);
return getCalibrationDataFromRepo().Any();
}
public event Action<StateOfDevice, string> StateChange;
private void UpdateStateChange(StateOfDevice state, string message = "")
{
var sc = StateChange;
if (sc != null)
{
StateChange(state, message);
}
}
}
public class Form
{
Label labelToModify;
void MagickButtonClick(object Source, EventArgs e)
{
CalibrationManager manager = new CalibrationManager(.......);
manager.loadFirmware.Progress += (o, e) => { labelToModify.Text = "Some progress achieved!!"; };
}
}