Custom main entry point parameters from the command line in C# - c#

I saw somewhere online someone showing what appeared to be valid C# code. It allowed for custom parameters in the main method to be passed as command-line arguments. It looked something like this:
static void Main(FileInfo input, int maxSize = 9, bool someflag = false)
{
// code
}
This could be used in the command line like this:
$ myApp hello_world.txt --maxSize 10 --someflag
Is this actually possible, or is someone pulling my leg?

Yes you can with DragonFruit, a (currently) alpha feature of the dotnet CLI.
Interpreting the string[] arguments into behaviors has been left as a task for the developer. Did the user ask for help? Did they pass invalid input? Can the input be converted to the types that you need if they're not string? These problems are not solved for you.
What if you could declare a strongly-typed Main method? This was the question that led to the creation of the experimental app model called "DragonFruit", which allows you to create an entry point with multiple parameters of various types and using default values [...]
Here's a tutorial:
https://github.com/dotnet/command-line-api/blob/main/docs/Your-first-app-with-System-CommandLine-DragonFruit.md

Related

Create new PayPal.Payments.DataObjects.TransactionResponse

Is it possible to create a new PayPal.Payments.DataObjects.TransactionResponse?
I'm currently working on upgrading (our old ERP system) to TLS 1.2, and I need to override a function that returns a PayPal.Payments.DataObjects.TransactionResponse, but PayPal.Payments.Communication.PayflowNETAPI.SubmitTransaction returns a string. Trying to simply create a new PayPal.Payments.DataObjects.TransactionResponse hasn't worked - I'm told in the VB code that:
'PayPal.Payments.DataObjects.TransactionResponse.Private Sub New()' is not accessible in this context because it is 'private'.
Trying in the C# code yields a less descriptive error:
'TransactionResponse' does not contain a constructor that takes 0 arguments
(replacing the 0 with any number of arguments that you put in -- I tried up to 8 or 9)
I am open to solutions in either VisualBasic or C#. Although the function in question is in VB, we opted to send our transactions to an internal processing server, which will return the string (written in C#), so I can do this from either side.
Basically, I just need to take the response (currently in string format), probably parse it (although a straight string conversion would be fine too), and put the info into a PayPal.Payments.DataObjects.TransactionResponse.
You have source code here.
You can check which parameter send to the constructor.
From a quick read of the Paypal SDK code mentioned by Ygalbel, it looks like there is no new() function. Rather, you would declare your var of type PayPal.Payments.DataObjects.TransactionResponse and then use the set and get accessors to set/get data in the class.

StringExpansion in c# called from Powershell

I have a small c# class, that does some logging stuff and that is called from a powershell scripting framework using:
[System.Reflection.Assembly]::LoadFrom("$ExtensionsPath\LogWriter.dll")
$Log = New-Object LogWriter($LogFile, $addTimeStamp, $logLevel, $overWrite)
Writing into the log file goes like this
$Log.AddInfo("myText")
Works fine so far.
What I am thinking about for some time is, if I am able to use stringexpansion in the AddInfo() method of my LogWriter class?
Look at the example:
$ModulesPath = ‘C:\temp\modules’
$test = ‘This is a text and I want to expand $ModulesPath in my c# LogWriter class’
$Log.AddInfo($test)
The c# class shall now expand the $modulespath in $test as powershell does. I already know that in c# I have access to the powershell runspace from which the c# class was called using System.Management.Automation Namespace. But then I am lost how to really expand the variable.
The entry written into the logfile should look like this:
This is a text and I want to expand C:\temp\modules in my c# LogWriter class
Of course I know I can do this in my script using
$Log.AddInfo(($ExecutionContext.InvokeCommand.ExpandString($test)))
But this is nasty because it looks ugly and if I forget to add this statement no expansion is done.
So I thought of retrieving the current Runspace in my c# class and do the ExpandString-Command there to get the expanded variable but I fail.
This is beyond my knowledge.
Anyone here to tell my if this is possible? I already think of some other tasks where to use this so please do not start a flame war about if this makes sense or not.
Rgds
Jan
How can the value for $ModulesPath be known outside of your script?
If you want it to be expanded in C#, then you have to send it, may be as a second Parameter to AddInfo like:
$Log.AddInfo($test, $ModulesPath)
Now it's known and the replacement could be done by:
string sNew = sTest.Replace("$ModulesPath", sModulesPath);
where sTest and sModulesPath are the parameters.
not sure if this is what you're asking about, but please try using double quotes on the $test string:
$test = "This is a text and I want to expand $ModulesPath in my c# LogWriter class"

Passing Arbitrary Arguments From C# to Lua Functions

I have discovered the cause of the issue. An answer has been posted below.
EDIT: The problem has changed, please see "The problem" section.
I am using LuaInterface. The generic call for lua functions using this library has this signature LuaFunction.Call(params object[] args). I have created a wrapper function that catches exceptions from the library and formats them for display on the in-game console window.
I am trying to call a lua function, but it is not receiving the arguments. This is the line in C#
Game.Instance.scriptEngine.Call("GenerateChunk", chunks[chunkID], GetChunkGridPosition(chunkID));
Which is simply wrapping a call to this Lua function that accepts two arguments:
//lua
function GenerateChunk(worldChunk, chunkGridPosition)
Log(LogLevel.Error, worldChunk.ToString());
Log(LogLevel.Error, chunkGridPosition.ToString());
end
that merely calls back into a C# Log function (which resolves correctly, and is visible in the Lua context).
The problem is that I am getting an "invalid arguments to method call" error from luainterface when attempting to call the GenerateChunk function, throwing this back:
invalid arguments to method call
at JASG.ScriptEngine.LuaError(Exception ex) Scripting\ScriptEngine.cs:line 144
at JASG.ScriptEngine.Call(String fnName, Object[] args) Scripting\ScriptEngine.cs:line 86
at JASG.ChunkManager.WakeChunk(Int32 chunkID) World\ChunkManager.cs:line 123
at JASG.ChunkManager.GetChunk(Int32 chunkID, Boolean wakeIfAsleep) World\ChunkManager.cs:line 53
I have tried various ways of calling the ScriptEngine.Call method, tried wrapping the arguments in an object[] array, etc., but no dice. Any ideas why lua is not receiving my arguments that I am passing? I have verified both arguments are non-null in C# when being passed in.
I've never used Lua before, but I've seen this kind of strange behaviors with calling COM objects (or any interop), or when the target assembly call is loaded on a different App Domain, or any other technology that intercommunicates a .Net assembly with a non-.Net one.
Have you tried using the [Serializable] attribute on the classes that define the result of "chunks[chunkID]" and "GetChunkGridPosition(chunkID)"? Are all your interop classes and types compatible between both assemblies?
Just thinking out loud here.
Side note: you should reduce your code to the shortest example that produces the problem. For instance, we don't need to see your wrapper function. You should have tried removing it. If that solved the problem, it's an important clue you should have been mentioned. If the problem remained, then that code is just a distracting irrelevancy for anyone reading this.
Your problem could be in your Log function. Everything thing else looks fine, that's the only code we can't actually see, and your problem can be reproduced like this:
public static void Log(int errorLevel, string message)
{
Console.WriteLine(message);
}
public void Test()
{
var lua = new Lua();
lua.RegisterFunction("Log", this, GetType().GetMethod("Log"));
lua.DoString("function foo() Log('a','b') end");
lua.GetFunction("foo").Call();
}
In this case, because 'a' cannot be marshaled into a number.
I incorrectly identified the problem as being with the call into Lua. The error message I was receiving was in fact originating from the Lua script calling back into my C# Log function.
I have discovered the hard way that in spite of exposing the enum LogManager.LogLevel to the lua script envronment, Lua does not support enum types. Thus,
Log(LogLevel.Debug, "hello");
was becoming
Log("Debug", "hello");
when marshalled by LuaInterface for the C# function. It was not until I created an ancillary ScriptLog(string level, string msg) that I was able to properly use the function from within lua. I wanted to keep the functionality of being able to use the enum names within Lua.
NOTE: As Lua does not support enum types, tonumber(LogLevel.Debug) fails as well.

C# default parameters workaround

Is there a workaround for default parameters? In C++ I would use
int foo(int k, bool check = false)
A tedious workaround would be to overload a function. An easier one? (There is no way just adding the variable and checking the calls of the function!!)
Thanks,
Sun
The C# (before 4.0) didn't support the default parameters. Even in c# 4.0 the default parameters are a bit different than in C++ - they're stored in metadata and, when you reference the assembly with default parameters, they're compiled into your code. So, if the default value was changed in the future, your code will still pass the OLD default value, which may cause the bad effect. So, use the overloaded functions with a single parameter and double parameters and call the one with more parameters passing the default value. Such approach will have a least side effect.
In C#4, you can do the same. So this is allowed:
int foo(int k, bool check = false){
...
}
There are also possible to use named arguments in C#4 so you can call this method in many different ways:
foo(10, true);
foo(10);
foo(k: 10, check: true);
foo(check: true, k: 10);
named arguments are useful if you have several optional parameters, and only want to specify one of them that is not the first optional one, or to improve readability on the calling side.
In C# 4.0 default and named parameters is supported now.
http://msdn.microsoft.com/en-us/library/dd264739.aspx
You can use optional parameters in your assemblies if they are build with MSBuild 4.0 (VS2010) even if you are targeting the .Net 2.0 framework.
The syntax is just like you said:
int foo(int k, bool check = false)
{
}
To elaborate on Denis Mazourick's answer about using default optional parameters in C# 4.0 and how the default values get compiled into the consuming class, try this.
Create a class library with the following code and build it:
public class ClassWithDefaultParameters {
public string Msg { get; set; }
public ClassWithDefaultParameters(string msg = "Hello World") {
Msg = msg;
}
}
public class ClassWithConstructorOverloads {
public string Msg { get; set; }
public ClassWithConstructorOverloads(string msg) {
Msg = msg;
}
public ClassWithConstructorOverloads() : this("Hello World") {}
}
Now create a console application and reference the dll you just built (not the project, but the actual dll). Place this in your code and build the console application.
static void Main() {
var cwdp = new ClassWithDefaultParameters();
var cwco = new ClassWithConstructorOverloads();
Console.WriteLine(cwdp.Msg);
Console.WriteLine(cwco.Msg);
}
When you run the application, the output will be as you expected:
Hello World
Hello World
Now open up the class library, and change both "Hello World" in "Hello Europe". Recompile the library and copy the dll to the output folder of the console application. Do not rebuild the console application.
When you run the console application again, the output will be:
Hello World
Hello Europe
Probably not what you expected! It's not until you rebuild the console application that both lines will print Hello Europe.
I didn't know this and I think I won't use the default parameters because of this. What's worse is that Microsoft doesn't mention this on the MSDN page.
well, there is no easier way, you could use param feature, but it is risky as well.
have a look at example for string.Format() where you can use it like:
stringA.Format("{0} is {1}", str1, str2)
that way you can pass any number of params, but it is quite tricky how u consume it and could be quite error prone

Is there .net magic to get parameter values by name in console application?

I've been developing .net console applications using C# and have always just dictated what order parameters must be inserted in so that args[0] is always start date and args[1] is always end date, for example.
however I would like to move over to using named parameters so that any combination of parameters can be sent in any order, such as the typical "-sd" would prefix a start date.
I know I could parse through the args[] looking for "-" and then read the name and look the next position for the accompanying value, but before doing that wanted to see if there was any kind of baked in handling for this rather standard practice.
is there something like this out there already that could do as such:
DateTime startDate = (DateTime)((ConsoleParameters)args[])["sd"]
I'm using C# and .Net 4
There is nothing built into the core framework.
A lot of people think NDesk.Options is useful for this sort of thing. Check out this example (taken directly from the provided link):
string data = null;
bool help = false;
int verbose = 0;
var p = new OptionSet () {
{ "file=", v => data = v },
{ "v|verbose", v => { ++verbose } },
{ "h|?|help", v => help = v != null },
};
List<string> extra = p.Parse (args);
Yes, the "magic" is that this is a common problem and it has been adequately solved. So I recommend using an already written library to handle parsing command line arguments.
CommandLineParser has been great for me. It is reasonably documented and flexible enough for every type of command line argument I've wanted to handle. Plus, it assists with usage documentation.
I will say that I'm not the biggest fan of making a specific class that has to be adorned with attributes to use this library, but it's a minor point considering that it solves my problem. And in reality forcing that attributed class pushes me to keep that class separate from where my app actually retrieves it's settings from and that always seems to be a better design.
You can use NDesk.Options.
There is no such a thing as named parameters. "-sd" is just a choice for a specific application. It can be "/sd" as well. Or "sd=". Or whatever you want.
Since there are no named parameters, there is nothing inside .NET Framework which let you use the "-sd" syntax.
But you can quite easily build your own method to get a set of "named parameters" for your app.
Edit: or, even better, you can use an existing library, like suggested in other answers.
Edit: reading the answer by #Sander Rijken, I see that I was wrong: there were still an implementation of "-sd" syntax in .NET 4.0 before the release. But since it was dropped before the final release, the only ways are still to create your own method or to use an existing library.

Categories