创建适用于System.Object的扩展方法的好习惯?

时间:2010-03-10 14:14:37

标签: c# .net extension-methods data-access

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

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),这些数据可以为null(不应该),因为底层数据库很遗憾非常这可能是空的。为了让我的生活更轻松,我想出了那些扩展方法。

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

提前谢谢你&最诚挚的问候:)

基督教

P.S。我没有故意将其标记为“主观”。

7 个答案:

答案 0 :(得分:7)

,这不是好习惯。您希望在可能的最低点应用扩展方法。我相信(几乎)一切都有时间和地点,但扩展方法 System.Object 几乎永远不合适。您应该能够在继承堆栈中进一步应用扩展方法。否则它会混乱你的智能感知,并可能最终被其他开发者错误地使用/依赖。

但是,用于处理Null值的数据对象的扩展方法是扩展方法的非常好用。考虑将它们放在 OleDbDataReader 上。我有一个名为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对象,请将扩展方法应用于 IDataRecord

答案 1 :(得分:5)

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

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

<小时/> 有一件事我个人觉得在我的CPU(我的大脑)被训练找到可能的NullReferenceException时,到处都在使用扩展方法。因为扩展方法看起来像实例方法,所以在阅读此类代码时,我的大脑通常会通过源代码解析器接收PossibleUseOfNullObject中断。在这种情况下,我必须分析NullReferenceException是否真的可以发生。这使得阅读代码变得更加困难,因为我经常被打断。

因此我对使用扩展方法非常保守。但这并不意味着我认为它们没有用。一定不行!我甚至写过广泛使用扩展方法的a library for precondition validation。当我开始编写该库时,我甚至在System.Objectinitially defined extension methods。但是,因为这是一个可重用的库并且由VB开发人员使用,所以我决定在System.Object上删除这些扩展方法。

答案 2 :(得分:5)

摘自“框架设计指南”一书

  

避免定义扩展方法   System.Object,除非绝对   必要。这样做时,请注意   VB用户将无法使用   如此定义的扩展方法,和   这样,他们将无法采取   可用性/语法优势的优势   随附扩展方法。

     

这是因为,在VB中,声明了一个   变量作为对象强制所有方法   对它的调用是迟到的 -   绑定到扩展方法时   是编译时确定的(早期   界)。例如:

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

在此示例中,对Foo的调用将失败   VB。相反,VB语法应该   简单地说:SomeExtensions.Foo(o)
  请注意,该指南适用于   其他语言相同的绑定   行为存在,或在哪里   不支持扩展方法

答案 3 :(得分:4)

在一般情况下,System.Object的扩展方法污染可能非常烦人,但您可以将这些扩展方法放在单独的命名空间中,以便开发人员必须主动选择使用这些方法。 / p>

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

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

答案 4 :(得分:2)

也许考虑将扩展方法添加到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);
    }
}

答案 5 :(得分:0)

除了已经提到的原因之外,应该记住VB中不支持System.Object上的扩展方法(只要变量静态类型为System.Object并且变量是变量不是静态类型为Object你应该更好地扩展另一个更具体的类型。)

此限制的原因是向后兼容性。有关详细信息,请参阅this question

答案 6 :(得分:-1)

我知道这不是最佳做法,但对于我的项目,我想包含此扩展名以转换数据类型,我发现它比转换类更容易..

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>();