我需要在运行时扩展各种类型的实例。大多数时候,我需要处理原始类型的实例,但是在一些情况下,我需要围绕那些添加了几个上下文信息的类型创建一种扩展包装器。下面的内容(实际上并不是有效的.NET / C#代码......但它说明了这一点):
public abstract class BaseClass
{
// ...
}
public class Concrete1: BaseClass
{
// ...
}
public class Concrete2: BaseClass
{
// ...
}
public class WrapperExtender<T>: T // Extending from T here is actually invalid!
where T: BaseClass
{
public WrapperExtender(T extensionTarget)
{
m_extensionTarget = extensionTarget;
}
private readonly T m_extensionTarget;
public object ContextualReference { get; }
public int ContextualValue { get; }
// DERP: Would need to implement overrides of T here...buuut...can't...
}
// In use, special case:
var instance = new Concrete1();
var extendedInstance = new WrapperExtender(instance);
var processor = new SomeProcessorRequiringExtendedInstance();
processor.DoProcessing(extendedInstance);
另一个例子可能是Microsoft Entity Framework v4.0或nHibernate。这两个框架都提供了实体类型的动态扩展实例,在内部包装它们,以便在运行时提供保持数据/对象/会话上下文与实体实例所做更改所需的挂钩。我的需求并不是那么复杂,如果只是某种方式将泛型和动态类型混合在一起,上面的泛型场景将会很好地工作。
无论如何,我希望有人知道如何实现上述方案。或者,甚至更好,有人知道更好的解决方案。我不太关心在运行时动态扩展类型的想法(它没有像在EF / nHibernate场景中那样有意义。)目前,它是我唯一能想到的东西这将为我提供处理器中传递给DoProcessing的每种类型所需的信息。
答案 0 :(得分:2)
EF等正在解决的问题是不同的,并且与延迟加载等问题有关。我只是不确定动态子类化所需的复杂程度对于这种情况是值得的。但是有一些想法:
ICustomTypeDescriptor
令人遗憾的是,C#不支持“mixins”,这也是使用接口实现此类事物的好方法。
答案 1 :(得分:1)
答案 2 :(得分:1)
如果您只需要一些额外的属性,为什么不在BaseClass中创建一个上下文属性?
类似这样的东西,其中ContextBag是泛型集合类或特别定义的上下文集合:
Public ContextBag Context
{
get;
set;
}
设置/访问上下文时,您将使用如下语法:
SubClass.Context.GetInt(ContextDefinition, ContextName);
SubClass.Context.Add(ContextDefinition, ContextName, ContextValue);
答案 3 :(得分:0)
找到比临时扩展更好的解决方案。我创建了一个实际的上下文对象,其中包含我需要的状态。每当上下文存在时,我初始化上下文并设置一个静态属性,可用于从任何地方检索上下文对象,从而减少更新我的大型进程的所有依赖关系以将上下文作为参数(不是'总是可能的,因为有时呼叫是在其他情况下进行的。)
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
}
}
我喜欢它的工作方式。上下文是行为执行的状态。我总是觉得笨拙的是必须将上下文数据与其他对象一起传递,并且有许多方法或方法重载,它们接收并传递各种形式的上下文数据。通过设置在该上下文持续时间内全局可用的上下文对象,我的代码更清晰,并且我的依赖关系更简洁。它也应该是可模拟的,因为Current属性是读/写的,我可以在BDD规范或TDD单元测试中创建一个模拟上下文,只要需要一个没有很多麻烦。