C# .NET工具:提取接口并实现包装器类

C# .NET工具:提取接口并实现包装器类,c#,unit-testing,interface,wrapper,C#,Unit Testing,Interface,Wrapper,是否有一个工具可以为现有类生成提取和生成接口 我知道VisualStudio将为现有类提取接口。但是,我还想生成一个实现该功能的包装器类 我相信这对单元测试有很大帮助 现有类示例: public class ThirdPartyClass { public void Method1(){} public void Method2(){} } public interface IThirdPartyClass { void Method1(); void Method2(

是否有一个工具可以为现有类生成提取和生成接口

我知道VisualStudio将为现有类提取接口。但是,我还想生成一个实现该功能的包装器类

我相信这对单元测试有很大帮助

现有类示例:

public class ThirdPartyClass
{
   public void Method1(){}
   public void Method2(){}
}
public interface IThirdPartyClass
{
   void Method1();
   void Method2();
}
public class ThirdPartyClassWrapper : IThirdPartyClass
{
   private tpc = new ThirdPartyClass();
   public void Method1()
   {
       tpc.Method1();
   }
   public void Method2()
   {
       tpc.Method2();
   }
}
这可以由Visual Studio生成(提取界面):

public class ThirdPartyClass
{
   public void Method1(){}
   public void Method2(){}
}
public interface IThirdPartyClass
{
   void Method1();
   void Method2();
}
public class ThirdPartyClassWrapper : IThirdPartyClass
{
   private tpc = new ThirdPartyClass();
   public void Method1()
   {
       tpc.Method1();
   }
   public void Method2()
   {
       tpc.Method2();
   }
}
我想更进一步:

public class ThirdPartyClass
{
   public void Method1(){}
   public void Method2(){}
}
public interface IThirdPartyClass
{
   void Method1();
   void Method2();
}
public class ThirdPartyClassWrapper : IThirdPartyClass
{
   private tpc = new ThirdPartyClass();
   public void Method1()
   {
       tpc.Method1();
   }
   public void Method2()
   {
       tpc.Method2();
   }
}
更新:

public class ThirdPartyClass
{
   public void Method1(){}
   public void Method2(){}
}
public interface IThirdPartyClass
{
   void Method1();
   void Method2();
}
public class ThirdPartyClassWrapper : IThirdPartyClass
{
   private tpc = new ThirdPartyClass();
   public void Method1()
   {
       tpc.Method1();
   }
   public void Method2()
   {
       tpc.Method2();
   }
}

这对于静态类特别有用。正如莫顿指出的那样,我可以只使用一个存根,但是,如果可能的话,我想断开我的耦合。

我不知道有什么工具可以为您做到这一点

您可能知道,但VisualStudio只前进了半步——它可以提供接口的空实现。如果是一次性任务,我就到此为止


根据实际目标,使用其他方法可能会起作用-例如,对于测试,您可以使用模拟框架-通常有一种方法可以包装现有类并根据需要重写某些方法。

您需要的是存根,这可以通过创建自己的接口存根实现来实现,或者使用类似Rhinomock的模拟框架。为了测试目的,在另一个类中包装一个困难的类对您没有任何好处

问候
Morten找到了一种解决非密封类问题的方法

1-从外部类继承

class MyWrapper : ExternalClass
class MyWrapper : IExternalClass
2-提取所有公共方法的接口

class MyWrapper : ExternalClass, IExternalClass
3-从外部类中删除继承

class MyWrapper : ExternalClass
class MyWrapper : IExternalClass
4-您将从未实现的接口中获得有关成员的类名提示。Alt+Enter并让Resharper自动实现它们

5-使用此代码模板包装属性

    get { return $INNERCOMPONENT$.$NAME$; }
    set { $INNERCOMPONENT$.$NAME$ = value; }
6-使用此代码模板包装方法

return $INNERCOMPONENT$.$NAME$($SIGNATURE$);

另一种非常巧妙的方法是使用Resharper为您生成“委派成员”,如下所述:

步骤:

  • 创建一个新类,该类从要使用该类类型的私有变量包装的类继承:

    public class ThirdPartyClassWrapper : ThirdPartyClass
    {
        private ThirdPartyClass _ThirdPartyClass;
    }
    
  • 在类中/上执行Alt插入,以使用Resharper生成“委派成员”。选择要公开并传递给私有变量的方法

  • 如果安装了免费版本的,则可以突出显示所有创建的属性、方法等,并执行Ctrl-D以自动从基类获取所有文档,并将其放在新成员上。(Resharper也可以做到这一点,但我认为您必须在每个项目上添加“new”,这将允许您Alt-Enter并从Resharper弹出菜单中选择“AddXMLDocComments”)

  • 然后,您可以删除基类并进行一些额外的清理,以防方法/属性签名公开您需要包装的任何其他类型


  • 我强烈建议您研究类似FakeiTasy的模拟框架

    但要给你你想要的,请看下文。我怀疑其他人接电话时ReSharper没有做这个手术

  • 将接口添加到希望作为包装类的类中

    class MyWebElement : IWebElement { }
    

  • 查找/单击“将“YourInterfaceHere”的实施委派给新字段

  • 选择您的选项

  • 单击“完成”并享受新课程

    class MyWebElement : IWebElement
    {
        private IWebElement _webElementImplementation;
        public IWebElement FindElement(By @by)
        {
            return _webElementImplementation.FindElement(@by);
        }
    
        public ReadOnlyCollection<IWebElement> FindElements(By @by)
        {
            return _webElementImplementation.FindElements(@by);
        }
    
        public void Clear()
        {
            _webElementImplementation.Clear();
        }
    
        public void SendKeys(string text)
        {
            _webElementImplementation.SendKeys(text);
        }
    
        public void Submit()
        {
            _webElementImplementation.Submit();
        }
    
        public void Click()
        {
            _webElementImplementation.Click();
        }
    
        public string GetAttribute(string attributeName)
        {
            return _webElementImplementation.GetAttribute(attributeName);
        }
    
        public string GetCssValue(string propertyName)
        {
            return _webElementImplementation.GetCssValue(propertyName);
        }
    
        public string TagName
        {
            get { return _webElementImplementation.TagName; }
        }
    
        public string Text
        {
            get { return _webElementImplementation.Text; }
        }
    
        public bool Enabled
        {
            get { return _webElementImplementation.Enabled; }
        }
    
        public bool Selected
        {
            get { return _webElementImplementation.Selected; }
        }
    
        public Point Location
        {
            get { return _webElementImplementation.Location; }
        }
    
        public Size Size
        {
            get { return _webElementImplementation.Size; }
        }
    
        public bool Displayed
        {
            get { return _webElementImplementation.Displayed; }
        }
    }
    
    类MyWebElement:IWebElement
    {
    私有iWebElementu WebElement实现;
    公共IWebElement FindElement(By@By)
    {
    返回_webElementImplementation.FindElement(@by);
    }
    公共只读集合FindElements(By@By)
    {
    返回_webElementImplementation.FindElements(@by);
    }
    公共空间清除()
    {
    _webElementImplementation.Clear();
    }
    公用void发送键(字符串文本)
    {
    _webElementImplementation.SendKeys(文本);
    }
    公开作废提交()
    {
    _webElementImplementation.Submit();
    }
    公共作废点击()
    {
    _webElementImplementation.Click();
    }
    公共字符串GetAttribute(字符串attributeName)
    {
    返回webElementImplementation.GetAttribute(attributeName);
    }
    公共字符串GetCssValue(字符串属性名称)
    {
    返回webElementImplementation.GetCssValue(propertyName);
    }
    公共字符串标记名
    {
    获取{return\u webElementImplementation.TagName;}
    }
    公共字符串文本
    {
    获取{return\u webElementImplementation.Text;}
    }
    公共布尔启用
    {
    获取{return\u webElementImplementation.Enabled;}
    }
    公共图书馆获选
    {
    获取{return\u webElementImplementation.Selected;}
    }
    公共点位置
    {
    获取{return\u webElementImplementation.Location;}
    }
    公共大小
    {
    获取{return\u webElementImplementation.Size;}
    }
    公开展览
    {
    获取{return\u webElementImplementation.Displayed;}
    }
    }
    

  • “将一个困难的类包装到另一个类中进行测试对您没有任何好处。“我认为这将减少耦合,并允许以后的自定义实现。是的,但为什么不现在就存根接口,让它作为测试的输入?如果您正在使用这个实现,那么您也在测试它,使您的测试成为一个集成测试。但是,我无法存根一个静态类。撇开测试不谈,我认为以后它会真正受益。如果输入类是静态的,那么可以通过委托注入静态函数。因此,在测试时,在静态类中使用方法可以替换为非静态/存根实现。关于它的任何解决方案都可以在这里找到完全相同的答案。我对这个问题投赞成票,再次投反对票。EF或VS应具有此选项。