2012-04-13 58 views
5

我一直在解决一个问题几个小时,现在我认为我很接近。我正在开发一款应用程序,我们可以使用50-100种类型来执行相同的操作。因此,而不是创建50-100班,我试图使它通用的,这是我所:在运行时创建具有通用接口的通用类型

这是基类:

public class RavenWriterBase<T> : IRavenWriter<T> where T : class, IDataEntity 

这是接口:

public interface IRavenWriter<T> 
{ 
    int ExecutionIntervalInSeconds { get; } 
    void Execute(object stateInfo); 
    void Initialize(int executionIntervalInSeconds, Expression<Func<T, DateTime>> timeOrderByFunc); 
} 

这是我如何使用它:

private static void StartWriters() 
{ 
    Assembly assembly = typeof(IDataEntity).Assembly; 
    List<IDataEntity> dataEntities = ReflectionUtility.GetObjectsForAnInterface<IDataEntity>(assembly); 

    foreach (IDataEntity dataEntity in dataEntities) 
    { 
     Type dataEntityType = dataEntity.GetType(); 
     Type ravenWriterType = typeof(RavenWriterBase<>).MakeGenericType(dataEntityType); 

     Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime; 

     // This is where I'm stuck. How do I activate this as RavenWriterBase<T>? 
     var ravenWriter = Activator.CreateInstance(ravenWriterType); 

     //ravenWriter.Initialize(60, func); // I can't do this until I cast. 

     // More functionality here (not part of this issue) 
    } 
} 

我被困在这条线上面:

var ravenWriter = Activator.CreateInstance(ravenWriterType); 

这是我的问题:
我如何使用,作为RavenWriterBase或IRavenWriter?喜欢的东西:

ravenWriter.Initialize(60, func); 

我认为它需要是这样的,但我需要指定一个类型IRavenWriter <>,我不知道它:

var ravenWriter = Activator.CreateInstance(ravenWriterType) as IRavenWriter<>; 

如果我将鼠标悬停在ravenWriter,我成功有我的对象:

enter image description here

但现在我需要能够在一个通用的方式来使用它。我怎样才能做到这一点?

更新:

我只是想使用动态关键字,而这个工程:

dynamic ravenWriter = Activator.CreateInstance(ravenWriterType); 
ravenWriter.Initialize(60); 

我被骗了一下,因为我意识到,Func键是为每个IDataEntity相同,因此这不需要作为参数传递给Initialize()。不过,至少现在我可以通过来调用Initialize()。但是现在Func是一样的,我也不需要通用接口。

+2

听起来像不正当使用的仿制药。当你发现自己需要根据不同的类型运行不同的方法,但只有在运行时才知道类型,你想要使用多态。什么是泛型获得你?您是否可以按照原样运行方法而不使用泛型,即使用IDataEntity? – mellamokb 2012-04-13 15:41:32

+1

我同意mellamokb。然而,如果你设置泛型,为什么不创建一个包装类,你可以实例化非genarically。包装应该有一个返回RavenWriterBase 的实例的方法,具体取决于作为参数传入的类型。然后,实例化包装器并以请求的类型作为参数调用该方法。这将需要一个大的switch语句,但至少不需要200个独立的类。 – dcow 2012-04-13 15:46:32

+2

您是否创建其他表达式不使用IDataEntity for T?或者T总是一个IDataEntity?如果T总是IDataEntity,为什么要使用泛型? – 2012-04-13 15:47:10

回答

2

我的解决办法是:

  • 创建的IRavenWriter
  • 非一般的接口,IRavenWriter
  • IRavenWriter<T>继承保持ExecuteExecutionIntervalInSecondsIRavenWriter
  • IRavenWriterFunc<DateTime>和使用在你的作家
  • M奥雅纳InitializeIRavenWriter<T>
  • 使用工厂根据类型和表达式初始化函数功能:

例如:

public class MyDateTime 
{ 
    public DateTime This { get; set; } 
} 

public static Func<DateTime> GetFunk<T>(Expression<Func<T, DateTime>> timeOrderByFunc, T t) 
{ 
    return() => timeOrderByFunc.Compile()(t); 
} 

你使用:

GetFunk<MyDateTime>(x => x.This, new MyDateTime(){This = DateTime.Now}); 
+0

我不确定我关注。这是怎么让我使用ravenWriter作为RavenWriterBase <>或IRavenWriter <>? – 2012-04-13 16:27:12

+0

@BobHorn它没有。你不能在那里了解RavenWriterBase <>'。相反,你需要在那里使用'IRavenWriterBase'(非泛型)。该类型的编译时知识不存在,因此您无法投射。 – Aliostad 2012-04-13 16:34:54

+0

谢谢。我刚刚编辑我的问题,以显示如何使用动态调用Initialize()。我认为这应该工作。 – 2012-04-13 16:36:57

1

这不是真的很难将运行时Type转换为编译时的泛型类型参数。与dataEntityType

 

interface IRawenWriterFactory 
{ 
    object Create(); 
} 

class RawenWriterFactory<T> : IRawenWriterFactory 
{ 
    public object Create() 
    { 
    Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime; 

    var ravenWriter = new RavenWriterBase<T>(); 
    ravenWriter.Initialize(60, func); 

    return ravenWriter; 
    } 
} 
 

现在只需创建RawenWriterFactory就像你已经创建ravenWriter,并通过非通用IRavenWriterFactory接口使用它:只要引进新的界面,用于创建/初始化你的对象。

但是,如果您要更改设计,可能会有更简单的解决方案。例如。如果将Initialize方法转换为构造函数,则可以将func作为Activator.CreateInstance参数传递,并且根本不需要使用通用接口进行初始化。