在运行时动态扩展类型?

时间:2009-08-23 05:19:55

标签: c# .net

我需要在运行时扩展各种类型的实例。大多数时候,我需要处理原始类型的实例,但是在一些情况下,我需要围绕那些添加了几个上下文信息的类型创建一种扩展包装器。下面的内容(实际上并不是有效的.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的每种类型所需的信息。

4 个答案:

答案 0 :(得分:2)

EF等正在解决的问题是不同的,并且与延迟加载等问题有关。我只是不确定动态子类化所需的复杂程度对于这种情况是值得的。但是有一些想法:

  • 在您的对象中有一个属性包,用于灵活的附加属性;如有必要,可以通过ICustomTypeDescriptor
  • 将属性包暴露给数据绑定API
  • 只需将对象包装在一个特定于实现的元组中,该元组包含现有对象和其他属性(无子类化)

令人遗憾的是,C#不支持“mixins”,这也是使用接口实现此类事物的好方法。

答案 1 :(得分:1)

我知道这可以使用dynamicproxy(NHibernate用来完成这项任务)完成,你可以在这里找到更多信息:

DynamicProxy Page

DynamicProxy tutorial

答案 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单元测试中创建一个模拟上下文,只要需要一个没有很多麻烦。