C# 在运行时动态扩展类型?
我需要在运行时扩展各种类型的实例。大多数情况下,我需要处理原始类型的实例,但是在少数情况下,我需要围绕这些类型创建一种扩展包装器,添加一些上下文信息。大致如下(实际上不是有效的.NET/C代码…但它说明了这一点):C# 在运行时动态扩展类型?,c#,.net,C#,.net,我需要在运行时扩展各种类型的实例。大多数情况下,我需要处理原始类型的实例,但是在少数情况下,我需要围绕这些类型创建一种扩展包装器,添加一些上下文信息。大致如下(实际上不是有效的.NET/C代码…但它说明了这一点): 公共抽象类基类 { // ... } 公共类1:基类 { // ... } 公共类2:基类 { // ... } 公共类WrapperExtender:T//从这里的T扩展实际上是无效的! 其中T:BaseClass { 公共包装扩展程序(T extensionTarget) { m
公共抽象类基类
{
// ...
}
公共类1:基类
{
// ...
}
公共类2:基类
{
// ...
}
公共类WrapperExtender:T//从这里的T扩展实际上是无效的!
其中T:BaseClass
{
公共包装扩展程序(T extensionTarget)
{
m_extensionTarget=extensionTarget;
}
私有只读m_扩展目标;
公共对象上下文引用{get;}
公共int上下文值{get;}
//DERP:需要在这里实现T的重写…buuut…不能。。。
}
//在使用中,特殊情况下:
var instance=new Concrete1();
var extendedInstance=newwrapperExtender(实例);
var processor=新的SomeProcessorRequiringExtendedInstance();
processor.DoProcessing(extendedInstance);
另一个例子可能是Microsoft Entity Framework v4.0或nHibernate。这两个框架都提供了实体类型的动态扩展实例,在内部包装它们,以便在运行时提供所需的钩子,使数据/对象/会话上下文与对实体实例所做的更改保持最新。我的需求几乎没有那么复杂,如果有一种方法能够以某种方式将泛型和动态类型混合在一起,那么上面的泛型场景将会很好地工作
无论如何,我希望有人知道如何实现上述场景。或者,也许更好的是,有人知道更好的解决方案。我不太喜欢在运行时动态扩展这样的类型(它不像在EF/nHibernate场景中那样有意义),这是我能想到的唯一一件事,它能为我提供处理器中传递给DoProcessing的每种类型所需的信息。我知道这可以通过使用dynamicproxy(NHibernate使用dynamicproxy完成此任务)来完成,您可以在这里找到更多信息:
如果您只需要一些附加属性,为什么不在基类中创建一个上下文属性呢 类似于此,其中ContextBag是通用集合类或特殊定义的上下文集合:
Public ContextBag Context
{
get;
set;
}
设置/访问上下文时,您将使用如下语法:
SubClass.Context.GetInt(ContextDefinition, ContextName);
SubClass.Context.Add(ContextDefinition, ContextName, ContextValue);
EF etc所解决的问题是不同的,并且与延迟加载等技巧有关。我只是不确定动态子类化所需的复杂程度是否值得用于此场景。不过,有几点想法:
- 在你的物品中放置一个属性袋,以获得灵活的附加属性;如有必要,可通过
ICustomTypeDescriptor
- 只需将对象包装在一个特定于实现的元组中,该元组包含现有对象和其他属性(无子类化)
遗憾的是,C#不支持“mixin”,这也是通过接口实现此类功能的一种好方法。找到了比暂时扩展更好的解决方案。我创建了一个实际的上下文对象,其中包含我需要的可用状态。只要该上下文存在,我就初始化该上下文并设置一个静态属性,该属性可用于从任何地方检索上下文对象,从而减少了更新更大进程的所有依赖项以将该上下文作为参数的需要(这并不总是可能的,因为有时调用是在其他上下文中进行的)
我喜欢这种工作方式。上下文是行为执行的状态。对于我来说,必须与其他对象一起传递上下文数据,并且有几十个方法或方法重载来接收和传递各种形式的上下文数据,这总是让我感到笨拙。通过设置一个在该上下文期间全局可用的上下文对象,我的代码更加简洁,依赖关系更加简洁。它也应该是可模拟的,因为当前属性是读/写的,所以只要需要,我就可以在BDD规范或TDD单元测试中创建一个模拟上下文,而不会有太多麻烦。数据是上下文的……它不属于基类,并且在执行代码不“在上下文中”的任何时候都不可用使其可用。因此,仅当在需要和可用额外数据的情况下进行调用时,才需要使用额外数据临时扩展类型。这不应阻止基类创建上下文包来保存上下文信息。也许我不够明确,上下文类应该是一个集合,我修改了示例代码来反映这一点。这是一个有趣的想法。在不同的环境中,它可能是唯一的选择。然而,我仍然不喜欢这样一个事实,即它在所有这些类型的实例中都放置短期的“上下文”数据,或者为这些数据打包。如果是始终可用的信息(即非上下文),那么我肯定会将其添加到基础中。我以前使用过DynamicProxy。相当强大的小图书馆。对我目前的需求来说有点过分了…:\我很感兴趣,你能详细说明你在做什么吗?看看我下面的答案。我找到了一个完美的解决方案,可以保持关注点的分离,并且不会混淆依赖关系。我也希望有“混合”。我想知道C#4.0的动态类型功能是否可能实现这样的事情…通过在接口上实现扩展方法,您可能会有一个糟糕的mans mixin…请注意,静态上下文对于线程化代码(如ASP.NET、WCF、,
SubClass.Context.GetInt(ContextDefinition, ContextName);
SubClass.Context.Add(ContextDefinition, ContextName, ContextValue);
public class SomeContext
{
public SomeContext(object stateData1, object stateData2)
{
StateData1 = stateData1;
StateData2 = stateData2;
}
public virtual object StateData1 { get; private set; }
public virtual object StateData2 { get; private set; }
[ThreadStatic]
private static SomeContext m_threadInstance;
public static SomeContext Current
{
get
{
return m_threadInstance;
}
set
{
if (value != null && m_threadInstance != null)
throw new InvalidOperationException("This context has already been initialized for the current thread.");
m_threadInstance = value;
}
}
}
public class SomeContextScope: IDisposable
{
public SomeContextScope(object stateData1, object stateData2)
{
if (SomeContext.Current == null)
{
SomeContext context = new SomeContext(stateData1, stateData2);
SomeContext.Current = context;
m_contextCreated = true;
}
}
private bool m_contextCreated;
public void Dispose()
{
if (m_contextCreated)
{
SomeContext.Current = null;
}
}
}
public class ComplexProcessor
{
public ComplexProcessor(...) // Lots of dependencies injected
public void DoProcessing(BaseClass instance)
{
// do some work with instance
if (SomeContext.Current != null)
{
// do contextually sensitive stuff for SomeContext with instance
// call a dependency that does contextually sensitive stuff
}
// do some more work with instance
// call a dependency that does contextually sensitive stuff
if (SomeOtherContext.Current != null)
{
// do contextually sensitive stuff for SomeOtherContext with instance
// call a dependency that does contextually sensitive stuff
}
// call a dependency that does contextually sensitive stuff
}
}
// The original setup of the context and initiation of processing
public void SomeOperation(...)
{
using (SomeContextScope scope = new SomeContextScope(stateData1, stateData2))
{
// ... do some work
var processor = complexProcessorFactory.CreateInstance();
processor.DoProcesing(data);
// ... do more work
}
}