2

I am working on logging exceptions. Now I have an ability to throw exceptions with any parameters and give them to log method. I want to display Name, Type and Value of each parameter in log-message. There are no problems with Type and Value, but I cannot get the Name of variable. I don't want to double the amount of parameters, adding nameof(Value) for each parameter when throwing.

This is how I throw with different parameters:

        throw new TestException(new object[]{ var0,
                                              var1,
                                              var2,
                                              var3,
                                              var4 });

This is how I don't want to throw them:

        throw new TestException(new object[]{ var0, nameof(var0)
                                              var1, nameof(var1)
                                              var2, nameof(var2)
                                              var3, nameof(var3)
                                              var4, nameof(var4) });

These are my thoughts about wrapping data in object:

public class ArgWrapper
{
    public ArgWrapper()
    {

    }

    public string Type { get; set; }
    public string Name { get; set; }
    public object Value { get; set; }
}

As result, I want to have collection of objects with such fields, so then I would log the info about each parameter like: { Type: "type", Name: "name", Value: "value" }

  • You could make it more readable with something like `throw new TestException().AlsoLog(x, nameof(x)).AlsoLog(y, nameof(y)) ...` Failing that see [Finding the variable name passed to a function](https://stackoverflow.com/questions/72121/finding-the-variable-name-passed-to-a-function) – Alex K. Apr 11 '19 at 14:33

3 Answers3

3

you need use GetProperties(), that method return all properties

object example  = new {si= DateTime.Now, no= "no"}; //object example

var typevar  = example.GetType().GetProperties(); //get all te props
//lets loop all the props because it GetProperties() return all props
foreach(var i in typevar){
     //if is DateTime well write the name of prop with i.Name 
     Console.WriteLine("The prop {0} is date time", i.Name);

}
Black Hole
  • 1,277
  • 12
  • 16
1

Add a method to the ArgWrapper class that can retrieve the information you need from an expression for the parameter. Like this:

public class ArgWrapper
{
    public static ArgWrapper Create<T>(Expression<Func<T>> expression)
    {
        T valueAsT = expression.Compile()();
        string value = Convert.ToString(valueAsT, CultureInfo.InvariantCulture);

        // Get type name
        string type = valueAsT?.GetType().FullName ?? "null";

        string name = "";

        // Get name by traversing memberexpressions from right to left
        MemberExpression memberExpression = expression.Body as MemberExpression;
        Expression traverseExpression = expression.Body;
        while (traverseExpression != null && traverseExpression is MemberExpression)
        {
            memberExpression = traverseExpression as MemberExpression;
            name = memberExpression.Member.Name + "." + name;
            traverseExpression = memberExpression.Expression;
        }

        // If the last memberexpression has no expression, this is a global class name, we want to include
        if (traverseExpression == null && memberExpression != null)
        {
            name = memberExpression.Member.DeclaringType.Name + "." + name;
        }

        var argWrapper = new ArgWrapper();
        argWrapper.Name = name;
        argWrapper.Type = type;
        argWrapper.Value = value;

        return argWrapper;
    }

    public string Type { get; set; }
    public string Name { get; set; }
    public object Value { get; set; }
}

If this is your exception class

public class TestException : Exception
{
    private List<ArgWrapper> parameters = new List<ArgWrapper>();
    public IEnumerable<ArgWrapper> Parameters => parameters;

    public TestException(params ArgWrapper[] passedParameters)
    {
        parameters.AddRange(passedParameters);
    }
}

you can then for example use it like this

private void button1_Click(object sender, EventArgs e)
{
    string s = "Hello World";
    double pi = Math.PI;
    string nullString = null;

    var exception = new TestException(
        ArgWrapper.Create(() => 42),
        ArgWrapper.Create(() => s),
        ArgWrapper.Create(() => s.Length),
        ArgWrapper.Create(() => pi),
        ArgWrapper.Create(() => button1.Text),
        ArgWrapper.Create(() => Application.CurrentCulture.Calendar.AlgorithmType),
        ArgWrapper.Create(() => DateTime.Now),
        ArgWrapper.Create(() => nullString)
        );


    foreach(var param in exception.Parameters)
    {
        Console.WriteLine($"{param.Name} - {param.Type} - {param.Value}");
    }

}

Which will produce this output:

 - System.Int32 - 42
s. - System.String - Hello World
s.Length. - System.Int32 - 11
pi. - System.Double - 3.14159265358979
button1.Text. - System.String - button1
Application.CurrentCulture.Calendar.AlgorithmType. - System.Globalization.CalendarAlgorithmType - SolarCalendar
DateTime.Now. - System.DateTime - 04/11/2019 18:23:42
nullString. - null - 
NineBerry
  • 26,306
  • 3
  • 62
  • 93
-1

The first part was correct. You need to define the properties as public and you need to know the object inlcuding the properties!

public Object TestProperty { get; set; } = "test";

private void GetFromName()
{
   var properties = this.GetType().GetProperties();
   var foundProperty = properties.ToList().Find(property => property.Name == /* nameof(x) string */);

   var test = foundProperty.GetValue(/* object containing the property */);
}
JohannesS
  • 11
  • 1