C# delegates functions being defined in an array declaration? - c#

I am making an IRC Chat bot for my stream. I found a few basic connectivity examples using C# so I decided to give it a try.
So far I love it
But i am stuck on this one part.
I want to store the bot commands inside an array of a structure type.
public delegate void cmdHandler(string[]);
struct botCommand
{
string name;
cmdHandler chandler;
bool isAdmin = false;
string help = "Nothing here.";
}
Is currently what I have, and then I want to beable to do this:
botCommand[]commands =
{
{ "TestCommand", testCommand(), 0, "Help for this" },
{ "TestCommand2", testCommand2(), 0 "..." },
......
};
So how do I link a generic function in that array?
or am I going about this all the wrong way?
Basically instead of having a giant Switch() statement to check for which command was used I want to loop through an array and see if the command is in there. If it is then call the function associated with that command.
EDIT:
This is exactly what I have now so you can see what I am trying to do
public delegate void cmdHandler(string[] ex);
struct botCommand
{
string name;
cmdHandler chandler;
bool isAdmin = false;
string help = "Nothing here.";
}
botCommand[] commands =
{
{"test", new cmdHandler(testf), 0, "" }
};
public void testf(string[] ex) {
return;
}
Steps of logic:
user enters the test command
Loop through all botCommands to see if we find the test command
Test command is found
Call the function associated with the test command and pass on an argument (the rest of the command)

To me it seems like you're mixing C/C++ concepts with C# (using struct instead of class, 0 for false, object initializers, etc...).
To solve your individual problem, you must instantiate your struct differently.
botCommand[] commands = new []
{
new botCommand {
name = "Test",
chandler = new cmdHandler(MyMethod),
isAdmin = false,
help = "No help for you..."
}
};
where MyMethod is defined as.
public static void MyMethod(string[] myArgs)
{
//... do something ...
}
However, I think a better approach would be to have an abstract class / interface for an individual command, list or dictionary of your commands.
public interface IBotCommand
{
string Name { get; }
bool IsAdmin { get; }
void Process(string[] args);
string HelpText { get; }
}
public class MyCommand : IBotCommand
{
string Name
{
get
{
return "NameOfTheCommand";
}
}
bool IsAdmin
{
get
{
return false;
}
}
void Process(string[] args)
{
// bla bla, im processing stuff
}
string HelpText
{
get
{
return "This is the help text";
}
}
}
And then using it.
List<IBotCommand> commands = new List<IBotCommand>();
commands.Add(new MyCommand());
// to find a command and execute it
IBotCommand cmdToExecute = commands.SingleOrDefault(c => c.Name == "NameOfTheCommand");
if (cmdToExecute != null)
{
cmdToExecute.Process(args); // where-ever args comes from
}
else
{
// unknown command "NameOfTheCommand"
}

Related

Is it possible to have method only accessible after certain conditions are met?

I'm trying to make a method, MethodA, only accessible when bool, executable, is true. Otherwise an other method, MethodB, is accessible. For example:
private bool executable = true;
public int MethodA(); <-- // Is accessible from outside of the class because executable is true
public string MethodB() <-- // Is not accessible because executable is true
The main reason I'm trying to do this is because the 2 methods return 2 different types. So my question is, is this even possible?
Option #1
You may be able to get what you want using Polymorphism and Generics. This would also allow you to add additional method strategies if needed.
public interface IMethodStrategy<out T>
{
T DoSomething();
}
public class MethodOneStrategy : IMethodStrategy<string>
{
public string DoSomething()
{
return "This strategy returns a string";
}
}
public class MethodTwoStrategy : IMethodStrategy<int>
{
public int DoSomething()
{
return 100; // this strategy returns an int
}
}
// And you would use it like so...
static void Main(string[] args)
{
bool executable = true;
object result = null;
if (executable)
{
MethodOneStrategy methodA = new MethodOneStrategy();
result = methodA.DoSomething();
}
else
{
MethodTwoStrategy methodB = new MethodTwoStrategy();
result = methodB.DoSomething();
}
}
Option #2
Another option could be a simple proxy method to wrap the worker methods.
// proxy class to wrap actual method call with proxy call
public class MethodProxy
{
public object DoMethodWork(bool executable)
{
if (executable)
{
return MethodA();
}
else
{
return MethodB();
}
}
private int MethodA()
{
return 100; // returns int type
}
private string MethodB()
{
return "this method returns a string";
}
}
// used like so
static void Main(string[] args)
{
var methodProxy = new MethodProxy();
object result = methodProxy.DoMethodWork(true);
}
Use conditional compilation for this.
#if RELEASE
public string MethodB() ...
#endif
Although I have my doubts about whether you need this or not. Your rationale doesn't make much sense.
You can use different Build Configurations to manage your conditional compile symbols.
if(executable)
MethodA();
else
MethodB();
OR
if(executable)
MethodA();
MethodB();
not entirely sure what you are trying to do but this could be one way, probably not the most efficient way but could work depending on what you are trying to do?
public int MethodA(executable)
{
if(executable = true)
{
//do stuff
}
else
{
return -1;
}
}
public String MethodB(executable)
{
if(executable = false)
{
//do stuff
}
else
{
String error = "MethodB cannot be used right now";
return error;
}
}

How do I refactor similar methods to eliminate duplicate setup and exception handling?

Context:
I consume a ERP WebService exposing N methods like:
FunctionNameResponse FunctionName(FunctionNameQuery query)
I made a functional wrapper in order to:
Get rid off wrapper object FunctionNameResponse and FunctionNameQuery, that every method has.
One instance of the WebService for all the program.
Investigate and log error in the wrapper.
Investigate Slow running and Soap envelope with IClientMessageInspector
Duplicated code:
For each of the methods of the WebService I end up with around thirty lines of code with only 3 distinct words. Type response, type query, method name.
public FooResponse Foo(FooQuery query)
{
// CheckWebServiceState();
FooResponse result = null;
try
{
result =
WSClient
.Foo(query)
.response;
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
I would like to reduce those repetition. In order to :
Make it easier to add a Method;
Avoid copy pasting programming with no need to understand what you are doing.
Easier to add specific catch and new test without the need to copy past in every method.
The following code work and exist only in the imaginary realm. It's a not functional sketch of my solution using my limited understanding.
public class Demo
{
public enum WS_Method
{
Foo,Bar,FooBar
}
public class temp
{
public Type Query { get; set; }
public Type Response { get; set; }
public WS_Method MethodName { get; set; }
}
public static IEnumerable<temp> TestFunctions =>
new List<temp>
{
new temp{Query=typeof(FooQuery), Response=typeof(FooResponse), MethodName=WS_Method.Foo },
new temp{Query=typeof(BarQuery), Response=typeof(BarResponse), MethodName=WS_Method.Bar },
new temp{Query=typeof(FooBarQuery), Response=typeof(FooBarResponse), MethodName=WS_Method.FooBar },
};
public static void Run()
{ // Exemple of consuming the method
var input = new BarQuery { Bar_Label = "user input", Bar_Ig = 42 };
BarResponse result = Execute<BarQuery, BarResponse>(input);
}
public static T2 Execute<T1,T2>(T1 param) {
//Get temp line where Query type match Param Type.
var temp = TestFunctions.Single(x => x.Query == typeof(T1));
var method = typeof(DemoWrapper).GetMethod(temp.MethodName.ToString(), new Type[] { typeof(T1) });
var wsClient = new DemoWrapper();
T2 result = default(T2);
try
{
result =
method
.Invoke(wsClient, new object[] { param })
.response;
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
}
I know the reflection is heavy and perhaps it's not the right way to achieve this refactoring. So the question is:
How do I refactor those function?
attachment : Live demo https://dotnetfiddle.net/aUfqNp.
In this scenario:
You have a larger block of code which is mostly repeated
The only difference is a smaller unit of code that's called inside the larger block
You can refactor this by passing the smaller unit of code as a Func or Action as a parameter to the larger function.
In that case your larger function looks like this:
public TResponse GetResponse<TResponse>(Func<TResponse> responseFunction)
{
var result = default(TResponse);
try
{
result = responseFunction();
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
The individual functions which call it look like this, without all the repeated code:
public FooResponse Foo(FooQuery query)
{
return GetResponse(() => WSClient.Foo(query));
}
Here's another approach where you keep the methods but have them all call a method that handles the duplication.
public class Demo
{
private _wsClient = new DemoWrapper();
public static void Run()
{ // Exemple of consuming the method
var input = new BarQuery { Bar_Label = "user input", Bar_Ig = 42 };
BarResponse result = Bar(input);
}
public FooResponse Foo(FooQuery foo) =>
Execute(foo, query => _wsClient.Foo(query));
public BarResponse Bar(BarQuery bar) =>
Execute(bar, query => _wsClient.Bar(query));
public FooBarResponse FooBar(FooBarQuery fooBar) =>
Execute(fooBar, query => _wsClient.FooBar(query));
private static TResponse Execute<TQuery ,TResponse>(
TQuery param, Func<TQuery, TResponse> getResponse)
{
//Get temp line where Query type match Param Type.
var result = default(TResponse);
try
{
result = getResponse(query);
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
}

.net: Setting a method to public gives an error. Setting it to private returns an error

class code
form code
homework assignment is to create a class and create a windows form that uses methods defined in the class. Here's his finished code.
class HappyBirthday {
//====================
// CLASS VARIABLES
//====================
private int numberOfPresents;
private string birthdayMessage;
private bool birthdayParty;
//===========================
// DEFAULT CONSTRUCTOR
//===========================
public HappyBirthday()
{
numberOfPresents = 0;
birthdayParty = false;
}
//===========================
// METHOD
//===========================
private string getMessage(string givenName)
{
string theMessage;
theMessage = "Happy Birthday " + givenName + "\n";
theMessage += "Number of presents = ";
theMessage += numberOfPresents.ToString() + "\n";
if (birthdayParty == true) {
theMessage += "Hope you enjoy the party!";
}
else {
theMessage += "No party = sorry!";
}
return theMessage;
}
//================================
// READ AND WRITE PROPERTY
//================================
public string MyProperty {
get { return birthdayMessage; }
set { birthdayMessage = getMessage(value); }
}
//================================
// WRITE-ONLY PROPERTY
//================================
public int PresentCount {
set { numberOfPresents = value; }
}
public bool hasParty {
set { birthdayParty = value; }
}
}
Well according to his code, string getMessage is private. Setting it to private makes it give an error on my form code button_1 "Inaccessible due to its protection level"
So I set it to public. That returns another error.
"There is no argument given that corresponds to the required formal parameter 'givenName' of 'HappyBirthday.getMessage(string)"
The method that "generates" the birthday message is private (which means it's only visible/accessible to the class it's defined in), it should be accessed via the MyProperty property.
In your Form1 class, change this:
MessageBox.Show( birthdayMessage.getMessage());
Into
MessageBox.Show(birthdayMessage.MyProperty);
And finally, the message is generated with a parameter for name (who the message is going to be for). You can set MyProperty property first, and it will generate the birthday for that name. So:
birthdayMessage.MyProperty = "Herbert";
var message = birthdayMessage.MyProperty; //<-- this now contains the full message
Your Problem is the call of the function:
public string getMessage (string givenName)
The method needs a String given to it upon calling it but you call it like this:
MessageBox.Show( birthdayMessage.getMessage());
When changing the call to
MessageBox.Show( birthdayMessage.getMessage("Clay"));
it should work.

C# run VBScript with MSScriptControl AddObject with string failed

This is my C# program:
class Program
{
static void Main(string[] args)
{
CallVbsFunction(1); //Work
CallVbsFunction(1.2); //Work
CallVbsFunction('a'); //Work
CallVbsFunction("a"); //!!Exception see bellow
}
private static void CallVbsFunction(object p)
{
var sc = new MSScriptControl.ScriptControl();
sc.Language = "VBScript";
sc.AllowUI = true;
try
{
sc.AddCode(System.IO.File.ReadAllText("script.vbs"));
sc.AddObject("myguid", p, false);
var parameters = new object[] { "a" };
sc.Run("test", ref parameters);
}
catch (Exception e)
{
Console.Out.WriteLine(e.ToString());
}
}
}
My VBScript file contents:
Function Test(a)
MsgBox myguid
End Function
And Finally this is my exception when I use AddObject() with string object:
System.Runtime.InteropServices.COMException (0x800A0005): Invalid
procedure call or argument at
MSScriptControl.IScriptControl.Run(String ProcedureName, Object[]&
Parameters) at Srcipting.Program.CallVbsFunction(Object p) in
Program.cs
You need to use a wrapper object that is ComVisible:
[ComVisible(true)]
public class StringWrapper
{
private string wrappedString;
public StringWrapper(string value)
{
wrappedString = value;
}
public override string ToString()
{
return wrappedString;
}
}
CallVbsFunction(new StringWrapper("a"));
The problem is that the .net String object looks like a native vb string to the MSScriptControl on the first look but not on the second look.
You only need to use this wrapper when you register a string directly or register a function that returns a string. There is no problem when registering an object that has properties of type string. There is also no problem for the parameters you pass to Run() because these will be correctly marshaled to native vb strings by the .net runtime.
So the maybe best option is to not provide individual strings to your script but an object that encapsulates all the different values you want it to use.
Define this class
[ComVisible(true)]
public class HostOptions
{
public string OptionA { get; set; }
public string OptionB { get; set; }
}
Then construct the object and set all the properties and register it with the script control
var hostOptions = new HostOptions();
hostOptions.OptionA = "AAA";
hostOptions.OptionB = "BBB";
sc.AddObject("HostOptions", hostOptions, false);
You can then use it in your script like this:
Function Test(a)
MsgBox HostOptions.OptionA
MsgBox HostOptions.OptionB
End Function

Return error from models

I have the following model:
internal static List<Contracts.DataContracts.Report> GetReportsForSearch(string searchVal, string searchParam)
{
var param1 = new SqlParameter("#SearchVal", searchVal);
var ctx = new StradaDataReviewContext2();
var reports = new List<Contracts.DataContracts.Report>();
try
{
//Validate param1 here and return false if the requirment are not met
}
catch(Exception e)
{
//Throw
}
}
param1 here Is a value entered by a user and I want to validate It here, and If the requirements are not met, I want to return an error.
But how can I return an error here from the model? The method Is of the type List, and I can't not just write return false in this method.
Any suggestion how to do It?
It is good that you didn't thought about throwing an exception, when requirements are not met. We shouldn't use exceptions for controlling program flow.
I have two options in my mind :
1. Use objects
Modify your GetReportsForSearch method to following signature:
internal static List<Contracts.DataContracts.Report> GetReportsForSearch(string searchVal,
string searchParam, ReportRequestor requestor)
{
var param1 = new SqlParameter("#SearchVal", searchVal);
var ctx = new StradaDataReviewContext2();
var reports = new List<Contracts.DataContracts.Report>();
try
{
//Validate param1 here and call RequirementsAreNotMet method if the requirements are not met
requestor.RequirementsAreNotMet();
}
catch(Exception e)
{
//Throw
}
}
And then you can implement code responsible for handling this situation in ReportRequestor class
public class ReportRequestor
{
public void RequiremenrsAreNotMet()
{
//code which handle situation when requiremenets are not met
}
}
2. Use return type as indicator of status
In this way, when requirements are not met you should create ReportGenerationStatus object with HasResult flag set to false.
In other case just set HasResult to true and also set results accordingly. This somewhat mimics Option type known from functional languages
internal static ReportGenerationStatus GetReportsForSearch(string searchVal, string searchParam)
{
//code for your method
}
public class ReportGenerationStatus
{
public List<Contracts.DataContracts.Report> Result { get; set; }
public bool HasResult { get; set; }
}

Categories

Resources