接受对象并返回另一个对象的通用委托

时间:2014-02-03 03:53:05

标签: c# generics delegates

考虑以下课程

public static class ObjectHelper 
{
    public static Func<DateTime> Now = () => DateTime.Now;

    public static Func<T, T> Clone = (obj) => Deserialize<T, T>(obj);

    public static Func<TIn, TOut> Deserialize = (obj) => BsonSerializer.Deserialize<TOut>(obj.ToJson());
}

CloneDeserialize声明无效。我用Google搜索了一下,但我似乎无法找到如何正确编写它们。我确信我错过了一些简单的事情。

我打算实现以下

  • 轻松切换Deserialize方法的正文。目前它使用MongoDb C#驱动程序中的类。我希望能够将其切换为使用说JSON.Net。不要担心是否有正当理由这样做或有任何更好的方法。在我的现实世界问题中,我已经解决了这个问题并继续前进。我只是想学习是否可以像上面那样编写代码。

我添加了Now来演示正常运行的代码。

1 个答案:

答案 0 :(得分:1)

第一个Now的作用原因是因为它不是通用的。

你在这里遇到了一些问题。

  • 您没有指定有关您的通用类型的任何信息(约束);
  • 例如,Deserialize如何知道obj(TOut类型)是否有方法,ToJson?
  • 此外,您有3种泛型:T,TIn和TOut。他们没有在任何地方宣布!并且,不需要其中一个。
  • 使用输入参数声明lambda时,请不要使用parens (),只需使用变量名称

解决问题的一些示例:

  1. 这是声明带参数的Lambda的正确方法:

    // No input parameter, use ()
    public static Func<DateTime> Now = () => DateTime.Now;
    
    // Input parameter(s), just name them and put them after the = sign
    public static Func<string, DateTime> Now = s => DateTime.Now;
    
  2. 为了使泛型类型能够使用方法,它必须实现一个接口和/或有一个约束,它继承了包含该方法的基类:

    public interface HasJson
    {
        string ToJson();
    }
    
    public static class ObjectHelper<TIn, TOut>
        where TIn : HasJson // now you can call ToJson() method on type TIn
    
  3. 您只需要2种泛型类型,TIn和TOut;不需要T,因为Clone采用相同的输入并返回与Deserialize相同的输出。这是一个有效的测试代码示例:

    public static class ObjectHelper<TIn, TOut>
        where TOut : class, new()
        where TIn : HasJson
    {
        // This Lambda is not generic; it'll work wherever you put it :)
        public static Func<DateTime> Now = () => DateTime.Now;
    
        // Clone takes TIn, and returns the output of Deserialize, which is TOut;
        // Therefore Clone and Deserialize will have the same generic type parameters
        public static Func<TIn, TOut> Clone = obj => Deserialize(obj);
    
        // calling obj.Json only possible due to where TIn: HasJson constraint above
        public static Func<TIn, TOut> Deserialize = obj => FakeBSON(obj.ToJson());
    
        // Just to test a method call in the generic lambda, meant to mimic what BSONSerializer 
        // does and allow my test code to compile without having BSONSerializer available
        static TOut FakeBSON(string json)
        {
            var res = new TOut();
            return res;
        }
    }