C# 包装类,同时仍公开其所有公共方法、属性和字段

C# 包装类,同时仍公开其所有公共方法、属性和字段,c#,generics,reflection,C#,Generics,Reflection,我正在尝试创建一个通用的包装器类,它可以包装任何其他类并向其添加额外的功能。然而,同时我希望能够在我通常使用包装类的任何地方使用这个包装。目前我使用隐式铸造,效果不错。但在理想情况下,我希望包装类与包装类具有相同的公开方法和字段,如下面的示例代码: class Foo { public int Bar() { return 5; } } class Wrapper<T> { private T contents; public void ExtraF

我正在尝试创建一个通用的包装器类,它可以包装任何其他类并向其添加额外的功能。然而,同时我希望能够在我通常使用包装类的任何地方使用这个包装。目前我使用隐式铸造,效果不错。但在理想情况下,我希望包装类与包装类具有相同的公开方法和字段,如下面的示例代码:

class Foo
{   
    public int Bar() { return 5; }
}

class Wrapper<T>
{
    private T contents;

    public void ExtraFunctionality() { }
    public static implicit operator T(Wrapper<T> w) { return w.contents; }
}



Foo f = new Foo();
Wrapper<Foo> w = new Wrapper<Foo>(foo);

int y = w.Bar();
class-Foo
{   
公共int-Bar(){return 5;}
}
类包装器
{
私有内容;
public void ExtraFunctionality(){}
公共静态隐式运算符T(包装器w){返回w.contents;}
}
Foo f=新的Foo();
包装w=新包装(foo);
int y=w.Bar();
我可以使用一些古老的巫术、反射术或其他诡计来使这成为可能吗


<>注释:在C++中,我只需重载->运算符,而不是在包装上操作字段内容。

,可以使用命名空间中的类动态生成类型。这可以为您提供一个良好的起点。

您可以使用命名空间中的类动态生成类型。这可以给你一个很好的起点。

我不推荐这样做,但既然你提到了巫术。。。您可以将动态键入与
DynamicObject
一起使用。包装器尝试处理请求的方法。如果不能,则将调用转发给底层包装对象:

class DynamicWrapper : DynamicObject
{
    private readonly object _contents;

    public DynamicWrapper(object obj)
    {
        _contents = obj;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        if (binder.Name == "ExtraFunctionality")
        {
            // extra functionality
            return true;
        }
        var method = _contents.GetType()
                             .GetRuntimeMethods()
                             .FirstOrDefault(m => m.Name == binder.Name);

        if (method == null)
        {
            result = null;
            return false;
        }

        result = method.Invoke(_contents, args);
        return true;
    }
}
编辑

没关系,我只是注意到你想在任何地方都使用这个实例,而你通常会使用包装类型的实例。
您必须将字段/属性/变量声明更改为
dynamic
,才能使用它。

我不建议这样做,但既然您提到了巫术。。。您可以将动态键入与
DynamicObject
一起使用。包装器尝试处理请求的方法。如果不能,则将调用转发给底层包装对象:

class DynamicWrapper : DynamicObject
{
    private readonly object _contents;

    public DynamicWrapper(object obj)
    {
        _contents = obj;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        if (binder.Name == "ExtraFunctionality")
        {
            // extra functionality
            return true;
        }
        var method = _contents.GetType()
                             .GetRuntimeMethods()
                             .FirstOrDefault(m => m.Name == binder.Name);

        if (method == null)
        {
            result = null;
            return false;
        }

        result = method.Invoke(_contents, args);
        return true;
    }
}
编辑

没关系,我只是注意到你想在任何地方都使用这个实例,而你通常会使用包装类型的实例。

您必须将字段/属性/变量声明更改为
dynamic
,才能使用它。

听起来像是一个修饰过的代理。使用T4生成包装类是一个选项?这是一个猜测,但是您是否尝试过
公共类包装器:T
?我不知道这是否可能。@Silvermind不,这是不可能的。@Iboshuizen我没有在web上下文中使用它,但我还没有考虑过代码生成。这可能是一个可行的解决方案,尽管我更喜欢纯C#解决方案,因为它更容易调试/发布/维护。包装类不能公开不是其定义imho显式部分的方法。也许[dynamic]可以起到解救作用,但使用T4可以实现早期绑定。此外,T4并不仅仅是一个用于网络的代理。听起来像是一个装饰过的代理。使用T4生成包装类是一个选项?这是一个猜测,但是您是否尝试过
公共类包装器:T
?我不知道这是否可能。@Silvermind不,这是不可能的。@Iboshuizen我没有在web上下文中使用它,但我还没有考虑过代码生成。这可能是一个可行的解决方案,尽管我更喜欢纯C#解决方案,因为它更容易调试/发布/维护。包装类不能公开不是其定义imho显式部分的方法。也许[dynamic]可以起到解救作用,但使用T4可以实现早期绑定。此外,T4并不仅仅是一个用于web的接口。但这将在运行时更改接口,而对于执行w.Bar()操作,我需要在编译时知道接口。如果生成的类型派生自包装类型或实现相同的接口,则不必更改接口。但是,您可能需要在客户机类中注入包装器类,以允许注入包装器类而不是原始类。一些IoC框架实现拦截,以允许在使用该方法的某些方法之前或之后运行代码我需要在编译时知道接口。如果生成的类型派生自包装类型或实现相同的接口,则接口不必更改。但是,您可能需要在客户机类中注入包装器类,以允许注入包装器类而不是原始类。一些IoC框架实现拦截,允许在使用这种方法的某些方法之前或之后运行代码。我明天要试试!(我还想知道这项工作的开销是多少,知道吗?)顺便问一下,您还需要覆盖
TryGetMember
TrySetMember
来获取/设置包装实例的属性。您所说的“强制类型重载”是什么意思?开销并没有那么高,我记得读过Eric Lippert的一篇文章,我认为一段动态代码将在运行时编译,然后尽可能缓存。在我的示例中,
public static implicit operator T(Wrapper w){return w.contents;}
。可以说是重载铸造。谢谢,最后我还是让用户手动完成了,但我从中真正学到了一些新东西,这也是一个合适的解决方案!只要我能为此写一个cast重载,就应该可以了。我明天要试试!(我还想知道这项工作的开销是多少,知道吗?)顺便问一下,您还需要覆盖
TryGetMember
TrySetMember
来获取/设置包装实例的属性。您所说的“强制类型重载”是什么意思?开销并没有那么高,我记得读过Eric Lippert的一篇文章,我认为一段动态代码将在运行时编译,然后尽可能缓存