代码之家  ›  专栏  ›  技术社区  ›  Christian

创建应用于System.Object的扩展方法的良好实践?

  •  9
  • Christian  · 技术社区  · 15 年前

    我想知道是否应该创建应用于对象级别的扩展方法,或者它们是否应该位于类层次结构中的较低点。我的意思是这样的:

    public static string SafeToString(this Object o) {
        if (o == null || o is System.DBNull)
            return "";
        else {
            if (o is string)
                return (string)o;
            else
                return "";
        }
    }
    
    public static int SafeToInt(this Object o) {
        if (o == null || o is System.DBNull)
            return 0;
        else {
            if (o.IsNumeric())
                return Convert.ToInt32(o);
            else
                return 0;
        }
    }
    //same for double.. etc
    

    我编写这些方法是因为我必须处理大量的数据库数据(来自oledbdatareader),这些数据可以为空(尽管不应该),因为底层数据库很不幸 非常 自由列,列可以为空。为了让我的生活简单一点,我想出了那些扩展方法。

    我想知道这是好的款式,可接受的款式还是差的款式。我有点担心它,因为它有点“污染”对象类。

    提前感谢并致以最良好的问候:)

    基督教的

    附:我不是故意把它贴上“主观”的标签。

    7 回复  |  直到 10 年前
        1
  •  7
  •   Patrick Karcher    15 年前

    ,这不是好的做法。您希望在 最低点 . 我相信每件事都有一个时间和一个地方,除了扩展方法 系统.对象 几乎永远都不合适。您应该能够在继承堆栈的更底层应用这样的扩展方法。否则,它会扰乱您的智能感知,并可能最终被其他开发人员错误地使用/依赖。

    但是,处理空值的数据对象的扩展方法是 很好的使用 扩展方法。考虑把它们放在你身上 数据阅读器 . 我有一个名为valueordefault的泛型扩展方法。…好吧,我给你看一下:

    <Extension()> _
    Public Function ValueOrDefault(Of T)(ByVal r As DataRow, ByVal fieldName As String) As T
        If r.IsNull(fieldName) Then
            If GetType(T) Is GetType(String) Then
                Return CType(CType("", Object), T)
            Else
                Return Nothing
            End If
        Else
            Return CType(r.Item(fieldName), T)
        End If
    End Function
    

    那是vb,但你知道了。这个吸盘为我节省了大量的时间,而且在读取数据行时,它确实能使代码变得干净。 你在正确的轨道上 ,但您的拼写感觉是正确的:扩展方法太高。

    将扩展方法放在单独的名称空间中总比什么都不做要好(这是对名称空间的完全有效的使用;linq使用了这个),但是您不必这样做。要使这些方法应用于各种db对象,请将扩展方法应用于 IDAT记录 .

        2
  •  5
  •   Matthew Groves    15 年前

    摘自《框架设计指南》一书

    避免在上定义扩展方法 系统。对象,除非绝对 必要的。这样做时,请注意 vb用户将无法使用 因此定义了扩展方法和 这样,他们就不能 可用性/语法优势 有扩展方法。

    这是因为,在vb中,声明 变量作为对象强制所有方法 对它的调用是后期绑定的 绑定到扩展方法时 编译时间是否确定(提前 绑定)。例如:

    public static class SomeExtensions{
         static void Foo(this object o){…} } … Object o = … o.Foo(); 
    

    在本例中,对foo的调用将失败 VB。相反,vb语法应该 简单地说就是:someextensions.foo(o)
    注意本指南适用于 相同绑定的其他语言 行为存在,或 不支持扩展方法

        3
  •  4
  •   Mark Seemann    15 年前

    在一般情况下,对象的扩展方法可能很烦人,但是您可以将这些扩展方法放在 分离命名空间 所以开发人员必须主动选择使用这些方法。

    如果将其与遵循单一责任原则的代码结合起来,则只需在相对较少的类中导入此命名空间。

    在这种情况下,这种扩展方法是可以接受的。

        4
  •  4
  •   Steven    10 年前

    这个 Framework Design Guidelines 建议你不要这样做。但是,这些指导原则特别适用于框架,因此如果您发现它在您的(业务线)应用程序中非常有用,请这样做。

    但请注意,在对象上添加这些扩展方法可能会使intellisense混乱,并可能会使其他开发人员感到困惑。他们可能不希望看到这些方法。在这种情况下,只需使用老式的静态方法:-)


    有一件事我个人觉得很麻烦,那就是我的CPU(我的大脑)被训练去寻找可能的 NullReferenceException 因为扩展方法看起来像实例方法,所以我的大脑经常收到 PossibleUseOfNullObject 由中断 源代码分析器 当读到这样的代码时。在这种情况下,我必须分析 空引用异常 实际发生与否。这使得阅读代码更加困难,因为我经常被打断。

    因此,我对使用扩展方法非常保守。但这并不意味着我认为它们没有用处。见鬼!我甚至写过 a library for precondition validation 广泛使用扩展方法。我甚至 initially defined extension methods System.Object 当我开始写那个图书馆的时候。但是,因为这是一个可重用的库,并且被vb开发人员使用,所以我决定在 系统.对象 .

        5
  •  2
  •   David Masters    15 年前

    或许可以考虑向idatarecord接口添加扩展方法?(oledbdatareader实现的)

    public static class DataRecordExtensions
    {
        public static string GetStringSafely(this IDataRecord record, int index)
        {
            if (record.IsDBNull(index))
                return string.Empty;
    
             return record.GetString(index);            
        }
    
        public static Guid GetGuidSafely(this IDataRecord record, int index)
        {
            if (record.IsDBNull(index))
                return default(Guid);
    
            return record.GetGuid(index);
        }
    
        public static DateTime GetDateTimeSafely(this IDataRecord record, int index)
        {
            if (record.IsDBNull(index))
                return default(DateTime);
    
            return record.GetDateTime(index);
        }
    }
    
        6
  •  0
  •   Community CDub    8 年前

    除了前面提到的原因之外,还应该记住扩展方法 System.Object 在vb中不受支持(只要变量静态类型为 系统.对象 如果变量不是静态类型的 Object 最好扩展另一个更具体的类型)。

    这种限制的原因是向后兼容。见 this question 更多细节。

        7
  •  0
  •   jaekie    15 年前

    我知道这不是最佳实践,但是对于我的项目,我喜欢在转换数据类型时包含这个扩展,我发现它比convert类容易得多。

    public static T ChangeType<T>(this object obj) where T : new()
    {
        try
        {
            Type type = typeof(T);
            return (T)Convert.ChangeType(obj, type);
        }
        catch
        {
            return new T();
        }
    }
    

    它会出现在每个对象中,但我通常只使用这个对象而不使用扩展列表。

    像这样工作…

    int i = "32".ChangeType<int>();
    bool success = "true".ChangeType<bool>();