Parametrizing string in .NET C# - c#

How to replace a method signature to accept parameterized strings without using param keywords. I have seen this functionality in Console.WriteLine().
e.g.
public void LogErrors(string message, params string[] parameters) { }
Scenario:
I have an error login function called
LogErrors(string message)
{
//some code to log errors
}
I am calling this function at different locations in the program in a way that the error messages are hardcoded. e.g.:
LogError("Line number " + lineNumber + " has some invalid text");
I am going to move these error messages to a resource file since I might change the language (localization) of the program later. In that case, how can I program to accept curly bracket bases parameterized strings? e.g.:
LogError("Line number {0} has some invalid text", lineNumber)
will be written as:
LogError(Resources.Error1000, lineNumber)
where Error1000 will be "Line number {0} has some invalid text"

Just call String.Format in your function:
string output = String.Format(message, parameters);

You probably want two methods:
public void LogErrors(string message, params string[] parameters)
{
LogErrors(string.Format(message, parameters));
}
public void LogErrors(string message)
{
// Use methods with *no* formatting
}
I wouldn't just use a single method with the params, as then it'll try to apply formatting even if you don't have any parameters, which can make things harder when you want to use "{" and "}" within a simple message without any parameters.

Basically use String.Format() method:
public void LogError(string format, params string[] errorMessages)
{
log.WriteError(String.Format(
CultureInfo.InvariantCulture,
format,
errorMessages));
}

Related

it is possible to get stacktrace of methods calls inside call method?

I want to add more info to the logger at the call method level, and i need to know if exist possibility to get StackTrace of methods calls inside call method.
UPDATE: The purpose of this is to draw the flow of all methods called until the certain step inside call method.
EXAMPLE:
public class Type1
{
internal string method2_T1() {
return new Type2().method1_T2();
}
}
public class Type2
{
public string method1_T2()
{
return "Type2.method1_T2";
}
}
static void Main(string[] args)
{
string t = new Type1().method2_T1();
LogNow();
....
}
and the result to obtain, when I call LogNow(), are:
StackTrace of method2_T1()
...
Thanks
It's pretty easy:
var stackTrace = new StackTrace(true);
var traceToLog = stackTrace.ToString();
The true argument says to include the file info.
Todd Sprang's answer is good as the actual answer, but be aware that the stack trace will change in unpredictable ways when you move to a RELEASE build, or use async/await. Don't rely programatically on the answers because you may come unstuck when you put the code into production.
If you want to know the direct caller of a particular function, in a way Microsoft recommend, there's the useful trick using the [CallerMemberName], [CallerFilePath], and [CallerLineNumber] attributes. Mark up optional parameters like so;
public void LogWithCallerInfo(
string message,
[CallerMemberName] string memberName = "Caller",
[CallerFilePath] string sourceFilePath = "File",
[CallerLineNumber] int sourceLineNumber = 0)
{
WriteProgressMessage(..., memberName, sourceFilePath, sourceLineNumber);
}
and call like this;
LogWithCallerInfo("my message");
The three optional parameters will be replaced with the appropriate call info.

Rename existing method returning void with input arguments

Maybe this is silly, but I'm trying to shorten the calling of the method StreamWriter.WriteLine becasue I have to call it many times throughout my code. So, instead of calling myFile.WriteLine() I would like to write just myFile.WL().
Is this possible?
After searching for a solution I wrote this:
private static void WL(this StreamWriter myFile, params string myString)
{
return (void)myFile.WriteLine(myString);
}
but I get the error
Cannot convert type 'void' to 'void'
Any ideas?
There is no need for the extension method, but just for the sake of completeness...
Remove the return statement from your method, as it doesn't have to return anything.
private static void WL(this StreamWriter myFile, params string myString)
{
myFile.WriteLine(myString);
}
BUT, Reconsider what you are trying to do. There is no point in shortening WriteLine to WL, it doesn't serve any purpose, it will make the code less readable, also you will not be able to cover up all the overloads.
Your Extension method should only be
private static void WL(this StreamWriter myFile, params string myString)
{
myFile.WriteLine(myString);
}
Reasons:
WriteLinemethod of StreamWriter does not return anything i.e. void. It only and I quote Writes a string followed by a line terminator to the text string or stream. So you should remove return keyword from the method. Read here.
BTW, Extension method should probably be public if you want to use it outside of the class you define it in.

Create string in MessageBox or WriteLine

I have a question, I don't really need it for the application at the moment but I was just curious.
Is there a way to create a string and fill it between the parentheses of WriteLine or Messagebox.Show ?
The code should then look something like this I think:
MessageBox.Show(String s = string.Format("Hello World"));
That is not the correct code, my only question is: Is something like that possible?
You can declare a string inside a call like that. However you can assign it.
string s = string.Empty;
MessageBox.Show(s = string.Format("Hello World"));
If you could declare strings inside a functioncall it wouldnt be visible elsewhere. So it woulndt really make any sense having that functionality in the language.
An alternative to Evelie's answer that lets you write it all in one line could be to define a helper method returning a string:
public static string ShowMsg(string msg) {
MessageBox.Show(msg);
return msg;
}
And your code would become:
string s = ShowMsg("Hello World");
or
string s = ShowMsg(string.Format("Now is {0}.", DateTime.Now));
And you could also perform the formatting inside your helper method:
public static string ShowMsg(string format, params object[] args) {
string mgs = string.Format(format, args);
MessageBox.Show(msg);
return msg;
}
And use it as:
string s = ShowMsg("Now is {0}.", DateTime.Now);

Is there a way to get an array of the arguments passed to a method?

Say I have a method:
public void SomeMethod(String p1, String p2, int p3)
{
#if DEBUG
object[] args = GetArguments();
LogParamaters(args);
#endif
// Do Normal stuff in the method
}
Is there a way to retrieve an array of the arguments passed into the method, so that they can be logged?
I have a large number of methods and want to avoid manually passing the arguments by name to the logger, as human error will inevitably creep in.
I'm guessing it will involve reflection in some form - which is fine, as it will only be used for debugging purposes.
Update
A little more information:
I can't change the method signature of SomeMethod, as it is exposed as a WebMethod and has to replicate the legacy system it is impersonating.
The legacy system already logs the arguments that are passed in. To start with the new implementation will wrap the legacy system, so I'm looking to log the parameters coming into the C# version, so that I can verify the right parameters are passed in in the right order.
I'm just looking to log the argument values and order, not their names.
If you use Postsharp you can simply add an attribute to the method you want to log. Within this attribute you can write the logging code and also will provide the arguments you need. This is known as cross cutting concerns and AOP (Aspect orientated programming)
I am unsure if the API to access the call stack provides a means to get the argument list.
However there are ways to inject IL to intercept method calls and execute custom code.
The Library I use frequently is PostSharp by Gael Fraiteur, it includes an application that runs postbuild and injects IL in your output assemblies depending on the Aspects that you are using. There are attributes with which you can decorate assemblies, types, or individual methods. For instance:
[Serializable]
public sealed class LoggingAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs eventArgs)
{
Console.WriteLine("Entering {0} {1} {2}",
eventArgs.Method.ReflectedType.Name,
eventArgs.Method,
string.Join(", ", eventArgs.Arguments.ToArray()));
eventArgs.MethodExecutionTag = DateTime.Now.Ticks;
}
public override void OnExit(MethodExecutionArgs eventArgs)
{
long elapsedTicks = DateTime.Now.Ticks - (long) eventArgs.MethodExecutionTag;
TimeSpan ts = TimeSpan.FromTicks(elapsedTicks);
Console.WriteLine("Leaving {0} {1} after {2}ms",
eventArgs.Method.ReflectedType.Name,
eventArgs.Method,
ts.TotalMilliseconds);
}
}
After this you can just decorate the method you want with this Attribute:
[Logging]
public void SomeMethod(String p1, String p2, int p3)
{
//..
}
Well, if you just want to pass the values, you can cheat and define an object array:
public static void LogParameters(params object[] vals)
{
}
This will incur boxing on value types and also not give you any parameter names, however.
Say I have a method:
public void SomeMethod(String p1, String p2, int p3)
{
#if DEBUG
LogParamaters(p1, p2, p3);
#endif
// Do Normal stuff in the method
}
Update: unfortunately reflection will not do it all automatically for you. You will need to provide the values, but you can use reflection to provide the param names/types:
How can you get the names of method parameters?
So the method sig would change to something like:
public static void LogParameters(string[] methodNames, params object[] vals)
{ }
Then you can enforce/assume that each index in each collection tallies, such that methodNames[0] has the value vals[0].
Well params help with the log call, but won't help the existing method signatures. Logging using an AOP framework might be a more productive approach?
Sure can ...check out this post, it gets the actual values of the params.
how to enumerate passed method parameters
There's some functionality with the dynamic type system that can do it, but then your class needs to inherit from the dynamic base classes
might not work in some scenarios but should get you started :)
class Program
{
static void Main(string[] args)
{
M1("test");
M2("test", "test2");
M3("test", "test2", 1);
Console.ReadKey();
}
static void M1(string p1)
{
Log(MethodBase.GetCurrentMethod());
}
static void M2(string p1, string p2)
{
Log(MethodBase.GetCurrentMethod());
}
static void M3(string p1, string p2, int p3)
{
Log(MethodBase.GetCurrentMethod());
}
static void Log(MethodBase method)
{
Console.WriteLine("Method: {0}", method.Name);
foreach (ParameterInfo param in method.GetParameters())
{
Console.WriteLine("ParameterName: {0}, ParameterType: {1}", param.Name, param.ParameterType.Name);
}
}
}
As long as you know what types to expect you could log them in an SQL database. Write a method that does a type check, and then fills the appropriate DB column with the parameter (argument) value. If you have a custom type then you can use the type name and save that as string in it's own special column.
-Edit
Also, using the MethodBase.Name extension method, you could associate your parameters with the method that took them as arguments as mentioned in another post below. Be a handy way of keeping track of all methods used, and with which arguments, and of which type.
Is this even vaguely a good idea? :)
Here's what I came up with as a solution:
PostSharp or another AOP solution wasn't really practical in this situation, so unfortunately I had to abandon that idea.
It appears that while it is possible to parameter names and types using reflection, the only way to access the runtime values is with a debugger attached.
See here for more info:
StackOverflow
microsoft.public.dotnet.framework
So that still left me with the problem of ~50 methods that needed this logging adding by hand.
Reflection to the rescue...
public String GetMethodParameterArray()
{
var output = new StringBuilder();
output.AppendLine();
Type t = typeof(API);
foreach (var mi in t.GetMethods())
{
var argsLine = new StringBuilder();
bool isFirst = true;
argsLine.Append("object[] args = {");
var args = mi.GetParameters();
foreach (var pi in args)
{
if (isFirst)
{
isFirst = false;
}
else
{
argsLine.Append(", ");
}
argsLine.AppendFormat("{0}", pi.Name);
}
argsLine.AppendLine("};"); //close object[] initialiser
output.AppendLine(argsLine.ToString());
output.AppendFormat("Log(\"{0}\",args);", mi.Name);
output.AppendLine();
output.AppendLine();
}
return output.ToString();
}
This code snippet loops through the methods on a class and outputs an object[] array initialised with the arguments passed into the method and a Log call containing the arguments and the method name.
Example output:
object[] args = {username, password, name, startDate, endDate, cost};
Log("GetAwesomeData",args);
This block can then be pasted into the top of the method to achieve the required effect.
It is more manual than I would have liked, but it is a lot better than having to type the parameters by hand and far less error prone.

How does System.TraceListener prepend message with process name?

I have been looking at using System.Diagnostics.Trace for doing logging is a very basic app. Generally it does all I need it to do. The downside is that if I call
Trace.TraceInformation("Some info");
The output is "SomeApp.Exe Information: 0: Some info". Initally this entertained me but no longer. I would like to just output "Some info" to console. So I thought writing a cusom TraceListener, rather than using the inbuilt ConsoleTraceListener, would solve the problem. I can see a specific format that I want all the text after the second colon. Here is my attempt to see if this would work.
class LogTraceListener : TraceListener
{
public override void Write(string message)
{
int firstColon = message.IndexOf(":");
int secondColon = message.IndexOf(":", firstColon + 1);
Console.Write(message);
}
public override void WriteLine(string message)
{
int firstColon = message.IndexOf(":");
int secondColon = message.IndexOf(":", firstColon + 1);
Console.WriteLine(message);
}
}
If I output the value of firstColon it is always -1. If I put a break point the message is always just "Some info". Where does all the other information come from?
So I had a look at the call stack at the point just before Console.WriteLine was called. The method that called my WriteLine method is: System.dll!System.Diagnostics.TraceListener.TraceEvent(System.Diagnostics.TraceEventCache eventCache, string source, System.Diagnostics.TraceEventType eventType, int id, string message) + 0x33 bytes
When I use Reflector to look at this message it all seems pretty straight forward. I can't see any code that changes the value of the string after I have sent it to Console.WriteLine. The only method that could posibly change the underlying string value is a call to UnsafeNativeMethods.EventWriteString which has a parameter that is a pointer to the message.
Does anyone understand what is going on here and whether I can change the output to be just my message with out the additional fluff. It seems like evil magic that I can pass a string "Some info" to Console.WriteLine (or any other method for that matter) and the string that output is different.
EDIT: I found the magic. Obviously it wasn't magic. The Write method gets call from a call to WriteHeader before the call to WriteLine which is where I thought the magic was happening.
You can get this by overriding the TraceEvent() method in your listener. Like this:
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message) {
Console.WriteLine("{0}: {1}", id, message);
}
You also need to override this TraceEvent overload if using formatting:
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType,
int id, string format, params Object[] data)
{
base.WriteLine(String.Format(format, data));
}
For example:
Trace.TraceInformation("Thread {0} Waiting", Thread.CurrentThread.ManagedThreadId);
Override will output output only "Thread 9 Waiting\n" when data[0] == 9
Why not use Trace.WriteLine("SomeInfo") instead of Trace.TraceInformation("SomeInfo") ?

Categories