C# 3.0 扩展方法是否隐藏依赖项?

C# 3.0 扩展方法是否隐藏依赖项?,c#-3.0,inversion-of-control,extension-methods,dependencies,testability,C# 3.0,Inversion Of Control,Extension Methods,Dependencies,Testability,全部, 我想听听你的想法。最近,在设计/开发时,我越来越认同“纯粹主义”的DI/IOC原则。这其中的一部分(很大一部分)涉及确保我的类之间很少有耦合,并且它们的依赖关系通过构造函数来解决(当然还有其他方法来管理它,但是你明白了) 我的基本前提是,扩展方法违反了DI/IOC的原则 我创建了以下扩展方法,用于确保插入数据库表的字符串被截断为正确的大小: public static class StringExtensions { public static string TruncateTo

全部,

我想听听你的想法。最近,在设计/开发时,我越来越认同“纯粹主义”的DI/IOC原则。这其中的一部分(很大一部分)涉及确保我的类之间很少有耦合,并且它们的依赖关系通过构造函数来解决(当然还有其他方法来管理它,但是你明白了)

我的基本前提是,扩展方法违反了DI/IOC的原则

我创建了以下扩展方法,用于确保插入数据库表的字符串被截断为正确的大小:

public static class StringExtensions
{
    public static string TruncateToSize(this string input, int maxLength)
    {
        int lengthToUse = maxLength;
        if (input.Length < maxLength)
        {
            lengthToUse = input.Length;
        }

        return input.Substring(0, lengthToUse);
    }
}
在不使用扩展方法的情况下,公平地翻译如下:

string myString = "myValue.TruncateThisPartPlease.";
StaticStringUtil.TruncateToSize(myString, 8);
任何使用上述示例的类都无法独立于包含TruncateToSize方法(TypeMock)的类进行测试。如果我没有使用扩展方法,并且我不想创建静态依赖项,那么它看起来更像:

string myString = "myValue.TruncateThisPartPlease.";
_stringUtil.TruncateToSize(myString, 8);
在最后一个示例中,将通过构造函数解析_stringUtil依赖关系,并且可以在不依赖于实际TruncateToSize方法的类的情况下测试该类(可以很容易地模拟它)

在我看来,前两个示例依赖于静态依赖关系(一个显式,一个隐藏),而第二个示例反转了依赖关系并提供了减少的耦合和更好的可测试性


那么,扩展方法的使用是否与DI/IOC原则相冲突?如果你是IOC方法的订户,你会避免使用扩展方法吗?

我知道你是从哪里来的,但是,如果你试图模仿扩展方法的功能,我相信你使用的方法是错误的。扩展方法应该用来执行一项任务,如果没有扩展方法,在语法上会很不方便。您的TruncateToLength就是一个很好的例子

测试TruncateToLength不需要模拟它,只需要创建几个字符串并测试该方法是否实际返回了正确的值

另一方面,如果您的数据层中的代码包含在访问数据存储的扩展方法中,那么是的,您有一个问题,测试将成为一个问题


我通常只使用扩展方法来为小而简单的操作提供语法上的支持。

我认为这很好,因为它不像
TruncateToSize
是一个实际可替换的组件。这是一种只需要做一件事的方法

您不需要能够模拟所有内容,只需要干扰单元测试(文件访问等)的服务,或者您希望根据真正的依赖性测试的服务。如果您使用它来执行身份验证或类似的操作,那将是一个非常不同的问题。。。但是,仅仅做一个完全没有可配置性、不同实现选项等的直字符串操作,就没有必要将其视为正常意义上的依赖关系


换一种说法:如果
TruncateToSize
String
的真正成员,您会三思而后行吗?您是否也尝试模拟整数运算,引入
iint32加法器
等?当然不是。这是一样的,只是您碰巧提供了实现。对
TruncateToSize
进行单元测试,别担心。

这很痛苦,因为它们很难模仿。我通常使用这些策略之一

  • 是的,把延长线给废了这是一个需要模仿的皮塔
  • 使用扩展并测试它是否做了正确的事情。i、 e.将数据传递到truncate,并检查它是否被截断
  • 如果这不是一件小事,并且我必须模拟它,我将使我的扩展类为它使用的服务设置一个setter,并在测试代码中设置它 i、 e


    这有点可怕,因为有些人可能会在不应该设置服务的时候设置服务,但我有时有点傲慢,如果这真的很重要,我可以使用#if测试标志或ServiceLocator模式做一些聪明的事情,以避免在生产中使用setter。

    扩展方法、分部类和动态对象。我真的很喜欢他们,但是你必须小心,这里有怪物

    我会看一看动态语言,看看它们如何在日常基础上处理这类问题,这真的很有启发性。特别是当他们除了良好的设计和纪律之外,没有什么可以阻止他们做愚蠢的事情的时候。在运行时,一切都是动态的,唯一能阻止它们的是计算机抛出一个主要的运行时错误。“Duck Typing”是我见过的最疯狂的事情,好的代码取决于好的程序设计、对团队中其他人的尊重,以及对每个成员的信任,尽管他们有能力做一些古怪的事情,但他们选择不做,因为好的设计会带来更好的结果

    对于使用mock objects/ICO/DI的测试场景,您真的会将一些繁重的工作放在扩展方法中,还是仅仅将一些简单的静态工作放在函数类型的方式中?我倾向于使用它们,就像你在函数式编程风格中那样,输入进去,结果中间没有魔术,只是直截了当的框架类,你知道MS的人已经设计和测试了:P,你可以依靠。p> 如果您正在使用扩展方法进行一些繁重的工作,我会再次查看您的程序设计,查看您的CRC设计、类模型、用例、DFD、动作图或您喜欢使用的任何东西,并找出在这个设计中您计划将这些内容放在扩展方法而不是适当的类中的什么位置

    最终,您只能根据您的系统设计进行测试,而不能编写超出范围的代码。如果要使用扩展类,我的建议是查看对象组合模型
    string myString = "myValue.TruncateThisPartPlease.";
    _stringUtil.TruncateToSize(myString, 8);
    
    static class TruncateExtensions{
    
        public ITruncateService Service {private get;set;}
        public string TruncateToSize(string s, int size)
        {
             return (Service ?? Service = new MyDefaultTranslationServiceImpl()). TruncateToSize(s, size);
        }
    }