C#动态调用通用方法

时间:2010-09-30 19:47:10

标签: c# generics generic-method

给出以下接口:

interface IEntity
{
    int Id{get;}
} 

interface IPerson : IEntity
{
    string Name{get;} 
    int Age{get;}
}

interface ITeacher : IPerson 
{
    string StaffId{get;}
}

interface IStudent : IPerson 
{
    string StudentId{get;}
    string Courses{get;}
}

interface IRepository
{
    T Get<T>(int id) where T : IEntity
}

我的命名空间中有以下类

public class EntityBase() : IEntity
{
    int Id{get;set;}
}
public class Teacher : EntityBase, ITeacher{}
public class Sudent : EntityBase, IStudent{}

目前,我正在按如下方式实施此IRepository:

class Repository: IRepository
{
    IDataContext Context{get;set;}

    T Get<T>(int id) where T : EntityBase
    {
        if(typeof(T) == typeof(Teacher))
            return Context.Get<ITeacher>(id);
        if(typeof(T) == typeof(Sudent))
            return Context.Get<ISudent>(id);
        throw new Exception("Unknown Interface " + typeof(T).Name);
    }
}

有更好的实施方法吗?鉴于我们的Context不知道我们的数据类型(教师,学生),只知道它的接口(ITeacher,IStudent)。

这样的事情可以吗?

class Repository: IRepository
{
    T Get<T>(int id) where T : EntityBase
    {
        var MyInterface = FindInterface<T>();
        return Context.Get<MyInterface>(id);
    }
}

4 个答案:

答案 0 :(得分:9)

我认为这样做:

class Repository: IRepository
{
    IDataContext Context{get;set;}

    T Get<T>(int id) where T : EntityBase    
    {
        string[] interfaceList = new string[] 
            { "ITeacher", "IStudent"};

        Type interfaceType = null;
        foreach (string s in interfaceList)
        {
            var types = typeof(T).FindInterfaces((x, y) => x.Name == y.ToString(), s);

            if (types.Length > 0)
                interfaceType = types[0];
        }

        if (interfaceType == null)
            throw new Exception("Unknown Interface " + typeof(T).Name);

        MethodInfo method = typeof(Context).GetMethod("Get");
        MethodInfo generic = method.MakeGenericMethod(interfaceType);

        var returnValue = generic.Invoke(Context, new object[] { id });

        return (T)Convert.ChangeType(returnValue, typeof(T));
    }
}

编辑:由于我不知道您的命名空间的名称,我使用了Name属性来过滤接口。在实际使用中,我建议您使用FullName以确保,如下所示:

...
string[] interfaceList = new string[] 
                { "MyNamespace.ITeacher", "MyNamespace.IStudent"};
...
var types = typeof(T).FindInterfaces((x, y) => x.FullName == y.ToString(), s);

答案 1 :(得分:5)

我认为你可以通过在Context类中找到Get方法并将其作为调用者提供的类型T的泛型调用来调用它来实现这一点。我没有测试过这个,但代码应该看起来像这样:

T Get<T>(int id) where T : EntityBase
{
    Type context = Context.GetType();

    MethodInfo getMethod = context.GetMethod("Get", BindingFlags.Public);
    MethodInfo genericGet = getMethod.MakeGenericMethod(new [] {typeof(T)});

    return (T)genericGet.Invoke(Context, new object[] { id } );
}

答案 2 :(得分:0)

在我看来,你的意思是反过来。您不希望将接口类型传递给Context.Get<>,是吗?

// is this what you mean?
if (typeof(T) == typeof(ITeacher))
    return Context.Get<Teacher>(id);

如果是,则需要使用MakeGenericMethod,请参阅this以获取示例(请注意缓存部分)。
如果不是,您可能会误解LINQ和/或Repository模式的一些概念。

但是我很好奇为什么你决定使用接口。无论如何LINQ对象都是POCO,为什么要添加另一层抽象,它涉及(grrsh!)通过反射调用DataContext上的泛型方法?

答案 3 :(得分:0)

简单的return Context.Get<T>(id)可以完成如下:

class Repository : IRepository
{
   public IDataContext Context { get; set; }



   public T Get<T>(int id) where T : IEntity, new()
  {



      return Context.Get<T>(id);



  }
}

以下是您的对象/接口模型,其中包含Context

的实现
 interface IEntity
{
    int Id{get;}
} 

interface IPerson : IEntity
{

}

interface ITeacher : IPerson 
{

}

interface IStudent : IPerson 
{

}

interface IDataContext
{
    T Get<T>(int id) where T:new();

}

interface IRepository  
{
    T Get<T>(int id) where T : IEntity , new() ;
}


public class EntityBase : IEntity
{
   public virtual int Id{get;set;}
}


public class Teacher : EntityBase, ITeacher {

    int id=0;
    public override int Id { 

                    get { return this.id; }

                    set { this.id = value; } 


                 }

}
public class Student : EntityBase, IStudent 
{
    int id=0;
    public override int Id {

                    get { return this.id; }

                    set { this.id = value; } 
                  }

}




class Context<T>: IDataContext where T: EntityBase,  new() 
{
    ArrayList store;


    public Context(int dataSize) 
    {
         store = new ArrayList(dataSize);

        for (int i = 0; i < dataSize; i++)
        {

            T t = new T();
            t.Id = i;           
            store.Add(t);


        }

    }    

    public T Get<T>(int i) where T:new()
    {
        if (i<store.Count)
        {   

            return (T)store[i]; 
        }

        else
        {
            return default(T);
        }

    }


}

现在终于主要的方法类来证明它们很好地挂在一起。

 using System;
 using System.Collections;

class MyClass
{

    static void Main(string[] args)
    {



        Context<Teacher> teachersContext  = new Context<Teacher>(100);//contructs a db of 100 teachers
        Context<Student> studentsContext = new Context<Student>(100);//contructs a db of 100 teachers 

        Repository repo = new Repository();




        // set the repository context and get a teacher

        repo.Context = teachersContext;
        Teacher teacher1 = repo.Get<Teacher>(83); //get teacher number 83
        Console.WriteLine("Teacher Id:{0} ", teacher1.Id);



        // redirect the repositry context and now get a student

        repo.Context = studentsContext;
        Student student1 = repo.Get<Student>(35); //get student  number 35

        Console.WriteLine("Student Id: {0} ", student1.Id);



        Console.ReadLine();

    }