【问题标题】:Using Generics in c# in order to avoid code duplication在 C# 中使用泛型以避免代码重复
【发布时间】:2011-05-09 20:26:19
【问题描述】:

(语言是 VS 2008 的 c#)

我有以下问题: 有许多结构(由第 3 方提供)都实现了具有相同签名的某些方法。 我想用实现某个接口的包装类包装这些结构,以便可以以统一的方式处理这些类。 示例:

interface AnInterface
{
    void DoSomething();
}

struct Struct1
{
    public void DoSomething();
}

class Struct1Wrapper : AnInterface
{
    private Struct1 m_struct;
    public override void DoSomething() // AnInterface implementation
    {
        m_struct.DoSomething();
    }
}

请注意,Struct1 DoSomething 方法是具体的,而 Struct1Wrapper 通过接口实现它以便于处理。

Struct2 等也是如此——StructXWrapper 的代码是一样的,只是 Struct1 被 StructX 替换了

我曾尝试使用泛型以避免代码重复:

class GenericStructWrapper<AStruct> : AnInterface
{
    private AStruct m_struct;

    public override void DoSomething() // AnInterface implementation
    {
        m_struct.DoSomething();
    }
}

但这不起作用,因为编译器没有关于 AStruct DoSomething() 方法的概念。

任何其他想法如何在不复制 Struct1Wrapper 代码的情况下实现这一点? 也许有一些类似宏的功能或使用反射?

谢谢,

乌里·杰姆斯。

【问题讨论】:

  • 您应该知道,当您将 valueType 作为接口引用时,valueType 每次都会被装箱。

标签: c# macros constraints generics


【解决方案1】:

您可以在采用该方法的类构造函数中采用Action&lt;AStruct&gt;

然后您可以创建像 new GenericStructWrapper&lt;Struct1&gt;(s =&gt; s.DoSomething()) 这样的实例

【讨论】:

    【解决方案2】:

    C# 不安全地 支持结构类型(除非在某些不寻常的上下文中),因此没有办法使这种完全安全没有 代码重复。您要么必须采用 SLak 要求 client 提供委托的技术(可能涉及一遍又一遍地重复相同的 lambda 表达式),要么假设底层类型将满足包含public void DoSomething() 方法的约定。

    使用第二个选项,这是在 C# 4 中使用 dynamic 的一种方法:

    public class StructWrapper: AnInterface
    {
        private readonly dynamic m_struct;
    
        public StructWrapper(object myStruct)
        {
            m_struct = myStruct;
        }
    
        public void DoSomething()
        {
            m_struct.DoSomething();
        }
    }
    

    现在,您可以尝试使这个类成为泛型,底层结构类型是泛型类型参数,但这可能对您没有太大帮助,除非您也 想要对包装类型执行特定于结构的 操作。这是一个带有反射和委托的示例(与 C# 3 兼容):

    public class StructWrapper<T> : AnInterface where T : struct
    {   
        private readonly Action action;
    
        // deliberately exposed
        public T UnderlyingStruct { get; private set; }
    
        public StructWrapper(T underlyingStruct)
        {
            UnderlyingStruct = underlyingStruct;
            action = (Action)Delegate.CreateDelegate
                      (typeof(Action), underlyingStruct, "DoSomething");
        }
    
        public void DoSomething()
        {
            action();
        }
    }
    

    请注意,您可以混合搭配上述两种技术,例如反射但没有泛型。

    用法:

    AnInterface wrapper1 = new StructWrapper(new Struct1());
    wrapper1.DoSomething();
    
    StructWrapper<Struct1> wrapper2 = new StructWrapper<Struct1>(new Struct1());
    wrapper2.DoSomething();
    Struct1 s = wrapper2.UnderlyingStruct; // generics help here
    s.SomeOtherMethod();
    

    【讨论】:

    • 请注意,对于 VS 2008,dynamic 尚不可用。您需要 VS2010 或安装 .NET 4.0 并直接从命令行编译该代码(通过 C# 编译器 csc.exe)。
    • @stakx:我提供了与 C# 3 兼容的替代方案,因此可以与 VS2008 一起使用。
    【解决方案3】:

    有一个语法:

    class GenericStructWrapper<AStruct> : AnInterface where AStruct : AnInterface
    { 
        private AStruct m_struct; 
    
        public override void DoSomething() // AnInterface implementation 
        { 
            m_struct.DoSomething(); 
        } 
    } 
    

    这表示 AStruct 必须实现 AnInterface

    【讨论】:

    • AStruct 表示的类型不实现 AnInterface - 我们无法更改这一点,因为这些是来自第三方库的结构,如问题中所述。
    最近更新 更多