2010-09-30 61 views
0

鑑於以下接口:C#調用泛型方法動態

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

有沒有更好的實施方式?鑑於我們的上下文不瞭解我們的數據類型(教師,學生),只知道它的接口(ITeacher,IStudent)。

可以這樣的工作?

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

這是這樣你就可以調用'Repository.Get (ID)',而不是'Repository.Get (ID)'?何必? – Gabe 2010-09-30 20:03:23

+0

是的。所以我也可以調用repository.Save(teacher)而不是repository。保存(老師) – 2010-10-01 14:02:41

+0

我仍然從未在實際項目中使用過依賴注入......但是從我讀過的內容看來,您感覺就像是重新實現了DI框架。 – Domenic 2010-10-06 06:10:28

回答

6

我認爲這會做:

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

編輯:由於我不知道你的命名空間的名稱,我使用的名稱屬性過濾的接口。在現實世界中的使用,我會建議你使用全名只是可以肯定,這樣的:

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

你的代碼不能編譯,因爲必須鍵入generic.Invoke輸入 – 2010-10-05 19:43:44

+0

我完全忘記了返回值。固定。 – Yogesh 2010-10-06 05:27:44

4

我認爲你可以找到在上下文類的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 }); 
} 
+1

是的,'genericGet' *必須被緩存,因爲你不想每次調用數據庫都得到'MethodInfo'。 – 2010-09-30 20:00:19

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上調用泛型方法的抽象層?

+0

你讀過這個問題了嗎? DataContext對「Teacher」沒有「知識」,只是ITeacher接口。 – 2010-10-01 14:04:14

+0

我可能會誤解某些東西,但我確信DataContext必須處理用LINQ to SQL屬性裝飾的類,而不是用接口。或者,您使用的不是LINQ to SQL嗎? – 2010-10-01 14:20:48

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



    } 
} 

以下是你的對象/接口模型與實施背景下的

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

    } 


} 

現在終於主要方法類脫monstrate,它全部掛在一起很好。

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

    }