0

I'm trying to understand a way to pass in two or more extension methods to a another method as parameters and return the value back. I have extension methods for each datatype to return a value or default value and a value or a null value and also a value or throw an error. The code has scenarios that would require each of these, but it also has scenarios combining the results from each of these in a ternary test, examples below.

public static int IntOrError(this object val, string fieldName)
{
    int test;

    if (string.IsNullOrEmpty(val.ToString()))
    {
        throw new Exception("I threw it");
    }
    else if (int.TryParse(val.ToString(), out test) == false)
    {
        throw new Exception("Bad Int Value");
    }
    else
    {
        return test;
    }
}

public static int IntOrDefault(this object val)
{
    int test;

    if (int.TryParse(val.ToString(), out test))
    {
        return test;
    }
    else
    {
        return -1;
    }
}

public static int? IntOrNull(this object val)
{
    int test;

    if (int.TryParse(val.ToString(), out test))
    {
        return test;
    }
    else
    {
        return -1;
    }
}

I've been trying to make a reusable method that could process taking in in this example IntOrNull, IntOrDefault and IntOrError and pass back the int or throw an error. So far I've only been able to get this to work. I'm trying to avoid creating this method for every datatype also.

public static int IntDefaultValidated(this object val, string fieldName)
{
    return val.IntOrNUll() != null 
        ? val.IntOrDefaultError(fieldName)
        : val.IntOrDefault();
}

I am trying to get a generic method or a functional style of method that will take in the extension methods as params and return back the value.

I'm hoping to avoid reflection if possible.

//just a psuedo example
var intVal = "";
var valRet = DefaultValidated(intVal.IntOrNull(), intVal.IntOrdefault(), intVal.IntOrDefaultError("intVal"));
//or maybe like this, or some combination of extension, generic, functional
var valRet = intVal.DefaultOrValidated(IntOrNull(intVal), IntOrDefault(intVal), IntOrDefaultError(intVal, "intVal"));
langc334
  • 50
  • 7

2 Answers2

0

Something like this?

public static T Convert<T>(this object input)
{
    if (typeof (T) == input.GetType())
        return (T) input;

    var stringValue = input.ToString();
    var converter = TypeDescriptor.GetConverter(typeof(T));
    if (converter.CanConvertFrom(typeof(string)))
    {
        return (T)converter.ConvertFrom(stringValue);
    }
    return default(T);
}

This test passes. May be this is not exactly what you need but still.

[Fact]
public void TestMethod1()
{
    object testInt = 1;
    object testString = "123";
    double testDouble = 1.0;
    string testWrong = "abc";

    int resultInt = testInt.Convert<int>();
    string resultString = testString.Convert<string>();
    int resultIntString = testString.Convert<int>();
    int dbl2int = testDouble.Convert<int>();

    Assert.Throws<Exception>(() => testWrong.Convert<int>());

    Assert.Equal(1, resultInt);
    Assert.Equal("123", resultString);
    Assert.Equal(123, resultIntString);
    Assert.Equal(1, dbl2int);
}
Alexey Zimarev
  • 17,944
  • 2
  • 55
  • 83
0

Your logic of IntOrDefault, IntOrNull and IntDefaultValidated does not really make sense, so I think it's just an usage example.
Beside this - my advice is to implement your functions as generic extensions.

public static class MyExtensions
{
    public static T ValueOrError<T>(this object val)
    {
        try
        {
            // http://stackoverflow.com/a/8633/2534462
            return (T)Convert.ChangeType(val, typeof(T));
        } catch
        {
            throw new Exception("Throw your own exception if you really want to");
        }
    }

    public static T ValueOrDefault<T>(this object val, T defaultValue)
    {
        try
        {
            return val.ValueOrError<T>();
        }
        catch
        {
            return defaultValue;  // usally use: return default(T);  
        }
    }

    public static T ValueOrNull<T>(this object val)
    {
        try
        {
            return val.ValueOrError<T>();
        }
        catch
        {
            // check for nullable type
            //https://msdn.microsoft.com/de-de/library/ms366789.aspx
            var type = typeof(T);
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return default(T);  // null on nullable types
            } else {
                throw new Exception("Callable only on Nullable types");
                // my return another default value ???  .. -1 ???
            }
        }
    }


    public static T ValueDefaultValidated<T>(this object val, T defaultValue)
    {
        return val.ValueOrNull<T>() != null
            ? val.ValueOrError<T>()
            : val.ValueOrDefault<T>(defaultValue);
    }
}

Usage

string aNumber = "10";
var intNumber = aNumber.ValueDefaultValidated(-1);  // int
var decNumber = aNumber.ValueDefaultValidated(-1m);    // decimal

string naNumber = "whatever";
var defaultInt = naNumber.ValueOrDefault(-1);  // int
var defaultDecimal = naNumber.ValueDefaultValidated<decimal?>(-1m);
// ValueOrNull ist undefined on Non-Nullable-Type. 
var undefined = naNumber.ValueDefaultValidated<decimal>(-1m);
Fried
  • 1,323
  • 12
  • 21
  • I'll have to adjust this so that the error handling works the same. In the original if someone past in a bad int of 123a. The Error extension method would throw it's Exception message that it was an invalid format. The answer logic here will throw the ValueOrNull Exception message instead, so not a complete one to one replacement. – langc334 Apr 06 '16 at 16:45