开发者

C# call Generic method dynamically [duplicate]

开发者 https://www.devze.com 2023-01-18 19:12 出处:网络
This question already has answers here: How do I use reflection to call a generic method? (8 answers) Closed 8 years ago.
This question already has answers here: How do I use reflection to call a generic method? (8 answers) Closed 8 years ago.

Given the following Interfaces:

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
}

I have the following classes in my na开发者_StackOverflow社区mespace

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

Currently I am implementing this IRepository as follows:

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);
    }
}

Is there a betterway of implementing this? Given that our Context has no knowledge of our data types (Teacher, Student), just its interfaces (ITeacher, IStudent).

Can something like this work?

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


I think this will do:

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));
    }
}

EDIT: As I don't know the name of your namespace, I have used the Name property to filter the interfaces. In real world usage I will suggest that you use FullName just to be sure, like this:

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


I think you can accomplish this through reflection by finding the Get method on Context class, and invoking it as a generic call for the caller-supplied type T. I haven't tested this, but the code should look something like this:

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 } );
}


It looks to me like you mean it the other way around. You don't want to pass interface types to Context.Get<>, do you?

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

If it is, you'll need to use MakeGenericMethod, see this for an example (note the caching part).
If it is not, you might be misunderstanding some concepts of LINQ and/or Repository pattern.

However I'm curious why did you decide to use interfaces. LINQ objects are POCO anyways, why adding another layer of abstraction which involves (grrsh!) calling generic methods on DataContext via reflection?


A simple return Context.Get<T>(id) could be accomplished as following:

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



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



      return Context.Get<T>(id);



  }
}

Following is your object/interface model with implementation for the 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);
        }

    }


}

Now finally the main method class to demonstrate that it all hangs together nicely.

 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();

    }
0

精彩评论

暂无评论...
验证码 换一张
取 消