有没有办法在C#中传递函数引用而不指定结果类型?

时间:2014-10-21 17:02:59

标签: c# generics .net-3.5 func

我有一个问题,我正在尝试编写一个读取xml并将数据写入数据库的通用方法。

在这种情况下,数据由另一个程序副本序列化。

在我将一个项目从xml添加到SqlParameter之前,我需要通过实用程序文件中的几个不同的“修复”方法之一来运行它。

需要的特定方法是需要传入的东西,因此我最终将其作为字符串传递并使用开关结构来确定使用反射调用哪个方法作为默认情况。

我想知道是否有任何方法可以实际传入方法引用,而不必指定它返回int,string,bool,decimal,double或DateTime。

我尝试过使用Func,但它需要一个结果类型。

新论点可能看起来像:    Tuple<string,Func<?>[] parameters

这些实用程序函数都将对象作为参数。

我发现了一个几乎我正在寻找的问题,除了他们没有一个可以完全不同的列表:

Func Return Any Type

我目前的代码:

private void SaveBundleToTable<T1, T2>
(
    string xmlData,
    string table,
    string storedProcedure,
    Tuple<string, string>[] parameters
)
    where T1 : DataSet, new()
    where T2 : DataRow
{
    T1 BundleData = new T1();
    BundleData.Clear();
    StringReader theReader = new StringReader(xmlDataset);
    BundleData.ReadXml(theReader);
    foreach (T2 item in BundleData.Tables[table].Rows)
    {
        SqlCommand _cmd = new SqlCommand(storedProcedure, [SQLConnection]);
        _cmd.CommandType = CommandType.StoredProcedure;
        foreach (var par in parameters)
        {
            var _param = getSqlParameter<T2>(item, par.First, par.Second);
            _cmd.Parameters.Add(_param);
        }
        _cmd.ExecuteNonQuery();
    }
}

以单独的方法切换结构:

private SqlParameter getSqlParameter<T>(T item, string columnName, string method) where T : DataRow
{
    switch (method)
    {
        case "FixString":
            return new SqlParameter(columnName, util.FixString(item[columnName]));
        case "FixInt":
            return new SqlParameter(columnName, util.FixInt(item[columnName]));
        case "FixBooleanToInt":
            return new SqlParameter(columnName, util.FixBooleanToInt(item[columnName]));
        case "FixBoolean":
            return new SqlParameter(columnName, util.FixBoolean(item[columnName]));
        case "FixFloat":
            return new SqlParameter(columnName, util.FixFloat(item[columnName]));
        case "FixDouble":
            return new SqlParameter(columnName, util.FixDouble(item[columnName]));
        case "FixDate":
            return new SqlParameter(columnName, util.FixDate(item[columnName]));
        case "FixDateForZeroTime":
            return new SqlParameter(columnName, util.FixDateForZeroTime(item[columnName]));
        case "FixDecimal":
            return new SqlParameter(columnName, util.FixDecimal(item[columnName]));
        default:
            try
            {
                Type thisType = util.GetType();
                MethodInfo theMethod = thisType.GetMethod(method);
                return new SqlParameter(columnName, theMethod.Invoke(this, new object[]{item[columnName]}));
            }
            catch (Exception ex)
            {
                throw new Exception("Unknown utility method: " + method,ex);
            }
    }
}

编辑1:

我刚尝试使用Tuple<string,Func<object,dynamic>>的参数类型,因为它无法确定Tuple<string,?>

的类型而无法编译

作为一个注释,这个项目在.Net 3.5中,所以我实现了本问题中讨论的Tuple的本地版本:

Equivalent of Tuple (.NET 4) for .NET Framework 3.5

编辑2:

如果我对泛型方法进行了更改,那么我可以传递一个类型为Tuple<string,Func<object,object>>的数组,那么当我尝试调用它时,我会在小数线上得到一个编译错误:

new Tuple<string, Func<object,object>>[]
   {
        Tuple.New<string, Func<object,object>>("LAB_CODE",util.FixString),
        Tuple.New<string, Func<object,object>>("MORM_SELL",util.FixDecimal),
   ...

如果我使用Tuple.New<string, Func<object,decimal>>

,则会出现更大的错误

3 个答案:

答案 0 :(得分:0)

你应该有一个util.Fix(Object o)函数,它通过以下方式确定代码来返回正确的修复:

if (o is string) return util.FixString(o);
else if (o is int) return util.FixInt(o) ...

它与您的相似,但它可以清理管理层。你被困在,你被迫执行某种if语句,因为你对每种数据类型做了不同的逻辑。

对于类,只需创建一个具有为修复定义的必要方法的接口。

答案 1 :(得分:0)

你能不能使用Func<object, object>吗?即使返回类型不是对象,我仍然可以编译它。以下是我在VS 2013上测试的示例:

public static string FixString(object data)
{
    return null;
}

static void Main(string[] args)
{
    DataRow row = null;
    SqlParameter param = getSqlParameter(row, "Address", FixString);
}

private static SqlParameter getSqlParameter<T>(T item, string columnName, Func<object, object> method) where T : DataRow
{
    return new SqlParameter(columnName, method(item[columnName]));
}

答案 2 :(得分:0)

我确实找到了一种让函数通过工作的方法,但对于我的情况来说这太冗长了。

new Tuple<string, Func<object,object>>[]
{
    Tuple.New<string, Func<object,object>>("LAB_CODE",util.FixString),
    Tuple.New<string, Func<object,object>>("MORM_SELL",x => (object)util.FixDecimal(x)),
...

除非我找到更简洁的方法,否则我会坚持使用我的switch语句。

编辑:

我将这些函数添加到我的静态Tuple类中:

public static Tuple<string, Func<object, object>> Util<T>(string first, Func<object, T> second)
{
    Func<object, object> method = getFunc<T>(second);
    var tuple = new Tuple<string, Func<object, object>>(first, method);
    return tuple;
}
private static Func<object,object> getFunc<T>(Func<object,T> method)
{
    return x => (object)method(x);
}

这让我可以使用这种语法传递参数:

new Tuple<string, Func<object,object>>[]
{
   Tuple.Util<string>("LAB_CODE",util.FixString),
   Tuple.Util<decimal>("MORM_SELL",util.FixDecimal),
...

这些可以在扩展函数中实现,以便与.Net 4 Tuple类一起使用