C#动态调用通用方法

Son*_*Ali 0 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
}
Run Code Online (Sandbox Code Playgroud)

我的命名空间中有以下类

public class EntityBase() : IEntity
{
    int Id{get;set;}
}
public class Teacher : EntityBase, ITeacher{}
public class Sudent : EntityBase, IStudent{}
Run Code Online (Sandbox Code Playgroud)

目前我正在实现这个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

这样的事可以吗?

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

Yog*_*esh 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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


Ada*_*ear 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 } );
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,并且 `genericGet` * 必须* 被缓存,因为你不想在每次数据库调用时都获取 `MethodInfo`。 (2认同)