9

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

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)(但不应该),因为不幸的是,底层数据库对可能为空的列非常自由。为了让我的生活更轻松一点,我想出了这些扩展方法。

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

提前谢谢你和最好的问候:)

基督教

PS我没有故意将其标记为“主观”。

4

7 回答 7

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

于 2010-03-10T14:18:07.897 回答
5

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

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


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

出于这个原因,我对使用扩展方法非常保守。但这并不意味着我认为它们没有用。一定不行!我什至编写了一个广泛使用扩展方法的前置条件验证库。我什至最初在开始编写该库时定义了扩展方法。System.Object但是,因为这是一个可重用的库并且被 VB 开发人员使用,所以我决定在System.Object.

于 2010-03-10T14:20:11.667 回答
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)
请注意,该指南适用于存在相同绑定行为或不支持扩展方法的其他语言

于 2010-03-10T14:22:04.090 回答
4

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

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

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

于 2010-03-10T14:21:06.947 回答
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);
    }
}
于 2010-03-10T14:23:25.990 回答
0

我知道这不是最佳实践,但对于我的项目,我喜欢包含这个用于转换数据类型的扩展,我发现它比 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>();
于 2010-03-31T17:37:15.850 回答
0

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

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

于 2010-03-10T14:25:17.257 回答