c#泛型方法将函数作为参数

时间:2016-03-29 18:48:38

标签: c# generics methods functional-programming extension-methods

我试图理解一种方法,将两个或多个扩展方法作为参数传递给另一个方法并返回值。我有每种数据类型的扩展方法,以返回值或默认值和值或空值以及值或抛出错误。代码的场景需要其中的每一个,但它也有一些场景,在三元测试中将每个场景的结果组合在一起,如下所示。

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;
    }
}

我一直在尝试创建一个可重用的方法,可以在此示例中处理IntOrNull,IntOrDefault和IntOrError,并传回int或抛出错误。到目前为止,我只能使这个工作。我试图避免为每种数据类型创建此方法。

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

我试图获得一个泛型方法或一种函数式方法,它将扩展方法作为params并返回值。

我希望尽可能避免反思。

//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"));

2 个答案:

答案 0 :(得分:0)

这样的东西?

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);
}

此测试通过。可能这不是你需要但仍然如此。

[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);
}

答案 1 :(得分:0)

你的IntOrDefault,IntOrNull和IntDefaultValidated的逻辑真的没有意义,所以我认为这只是一个用法例子。
除此之外 - 我的建议是将您的功能实现为通用扩展。

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);
    }
}

用法

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);