如何使用泛型类型传递一个函数来调用方法?

时间:2015-08-01 13:36:17

标签: c# generics dependency-injection delegates

编辑:我刚刚意识到我想要做的是遵循我从Mark Seemann学到的依赖注入模式,该模式在该主题上写了excellent book。我想有一个方法,我做一些设置,如添加行,但传入一个将行写入上下文的对象。 BulkInsert方法适用于大型"设置"而AddRange适用于较小的设置。

我有一个静态方法,我在我的DbContext中传递,并且我将行更新为多个实体。根据我调用方法的方式,我想使用BulkInsertAddRange

添加行
public static void MySetup(MyContext, int addMethod)
{
    var list1 = new List<Foo>{......}
    if (addMethod = 1)
       context.BulkInsert(list1);
    else
       context.AddRange(list1);

    var list2 = new List<Bar>{......}
    if (addMethod = 1)
       context.BulkInsert(list2);
    else
       context.AddRange(list2);

}

我不想传入变量,而是想学习如何传递函数,所以它想要这样的东西:

public static void MySetup(MyContext context, ??? myAdd)
{
    var list1 = new List<Foo>{......}
    myAdd(context, list1);

    var list2 = new List<Bar>{......}
    myAdd(context, list2);
}

认为我正在寻找的是代表。请注意,我使用不同类型的List类,因此我在调用端设置的任何函数都需要接受通用列表(是吗?)因为我不想为每种可能的类型传递一个函数。

ETA:

BulkInsert和AddRange都是通过EntityFramework添加行。他们每人都拿一份清单。 BulkInsert有一个可选的选项参数,因此可以调用context.BulkInsert(myList, opts)

3 个答案:

答案 0 :(得分:3)

认为你需要的只是一个接受IList的代表,但如果不知道AddRangeAddBulk方法的签名就很难知道

public static void MySetup(MyContext context, Action<MyContext, IList> myAdd)
{
    var list1 = new List<Foo> { };
    myAdd(context, list1);

    var list2 = new List<Bar> { };
    myAdd(context, list2);
}
...

Action<MyContext, IList> insertRange = (context, list) =>
{
    context.AddRange(list);
};

Action<MyContext, IList> insertBulk = (context, list) =>
{
    context.BulkInsert(list);
};

MySetup(new MyContext(), insertRange);
// or...
MySetup(new MyContext(), insertBulk);

答案 1 :(得分:1)

我很想过我是否应该发布它(因为它有点回答问题,但我不认为它解决了背后的问题)但是就这样吧:

如果你知道你将要使用的所有可能的类型,最简单的方法是:

interface IAddStuff 
{
    void Add (MyContext context, IList<Foo> list);
    void Add (MyContext context, IList<Bar> list);
}

class Operations
{
    public static void MySetUp(MyContext context, IAddStuff adder)
    {
        var list1 = new List<Foo> ();
        adder.Add (context, list1);

        var list2 = new List<Bar> ();
        adder.Add (context, list2);
    }
}

class SampleImplementation : IAddStuff 
{
    public void Add(MyContext context, IList<Foo> list)
    {
        // ...
    }

    public void Add(MyContext context, IList<Bar> list)
    {
        // ...
    }
}

现在,如果你不确切地知道你仍然可以进行运行时调度(并让实现决定):

class MyContext {}
class Foo {}
class Bar {}

interface IAddStuff 
{
    void Add<T> (MyContext context, IList<T> list);
}

class Operations
{
    public static void MySetUp(MyContext context, IAddStuff adder)
    {
        var list1 = new List<Foo> ();
        adder.Add (context, list1);

        var list2 = new List<Bar> ();
        adder.Add (context, list2);
    }
}

class SampleImplementation : IAddStuff 
{
    public void Add<T> (MyContext context, IList<T> list)
    {
        if (typeof(T) == typeof(Foo)) 
            AddFoo(context, list.Cast<Foo>());
        else if (typeof(T) == typeof(Bar)) 
            AddBar(context, list.Cast<Bar>());
    }

    void AddFoo(MyContext context, IEnumerable<Foo> list)
    {
        // ...
    }

    void AddBar(MyContext context, IEnumerable<Bar> list)
    {
        // ...
    }
}

现在你可以看到你只是将问题(运行时检查你真正拥有的通用类型)移动到所述接口的实现中,但是你可以保持你的其他代码更干净。

不知道你是否认为这有用,但这是最近的可以达到我认为的你问的

PS 应该明白如何使用某个参数更新此信息,以判断是否应使用 bulk-insert

PPS ,是的,您可以滥用 dynamic内容,以便在没有if的情况下进行动态调度......但我不知道不太喜欢那些 - 如果你对此感兴趣的话,那么an example where this trick is used

答案 2 :(得分:0)

如果Foo和Bar共享一个公共接口,那么您可以使用Action delegate传递该方法。

例如:

public interface IBaz
{
}

public class Foo : IBaz
{     
}

public class Bar : IBaz
{
}

public static void MySetup(DurianContext context, Action<DurianContext, List<IBaz>> addMethod)
{            
    List<IBaz> list1 = new List<IBaz>();
    addMethod(context, list1);

    List<IBaz> list2 = new List<IBaz>();
    addMethod(context, list2);
}