将对象强制转换为指定的类最佳实践

本文关键字:最佳 对象 转换 | 更新日期: 2023-09-27 18:18:45

我有一个方法,可以获得各种类型的object。根据typeobject的不同,我需要做不同的动作。我当前的代码是这样的:

public void SomeMethod(object obj)
{
    int? someId = null;
    Class1 class1 = obj as Class1;
    Class2 class2 = obj as Class2;
    Class3 class3 = obj as Class3;
    Class4 class4 = obj as Class4;
    if (class1 != null && class1.SomeProperty != null)
    {
        someId = class1.SomeProperty.Id;
    }
    else if (class2 != null && class2.AnotherProperty != null)
    {
        someId = class2.AnotherProperty.AnotherId;
    }
    ...
    AnotherMethod(someId);
}

我对这段代码不太满意,因为我确实有一些不必要的强制转换。做这种手术的最有效的方法是什么?我在想这样写:

if (obj.GetType().Equals(typeOf(Class1))
{
    someId = ((Class1)obj).SomeProperty.Id;
}

有什么建议吗?

将对象强制转换为指定的类最佳实践

这两种替代方法都非常糟糕,因为它们进行显式强制转换,并且通过手动检查类型来分派。这很容易出错,并且当您展开方法需要处理的类型列表时,会导致不可避免的维护问题。

从。net 4.0开始,你有一个更好的选择——使用dynamic分派:

public void SomeMethod(dynamic obj) {
    SomeMethodImpl(obj);
}
private void SomeMethodImpl(Class1 obj) {
    // Perform actions specific to Class 1
}
private void SomeMethodImpl(Class2 obj) {
    // Perform actions specific to Class 2
}
private void SomeMethodImpl(Class3 obj) {
    // Perform actions specific to Class 3
}
private void SomeMethodImpl(Class4 obj) {
    // Perform actions specific to Class 4
}
private void SomeMethodImpl(object obj) {
    // Catch all
}

现在,您可以将特定于类的代码放置在其自身的SomeMethodImpl重载中,而无需运行条件语句链。扩展你的方法来处理额外的类型也很简单——你所需要做的就是添加另一个重载。

注意:我假设SomeMethod的签名必须保持不变——换句话说,您不能仅仅通过重载 SomeMethod来解决这个问题。

多态性??

public void SomeMethod(Class1 class1)
{
    AnotherMethod(class1.SomeProperty.Id);
}
public void SomeMethod(Class2 class2)
{
    AnotherMethod(class2.NewProperty.Id);
}
.
.
.

为什么不调用AnotherMethod(class1.SomeProperty.Id);,…AnotherMethod(class2.NewProperty.Id)直接,如果这是所有你想做的…

如果你真的想检查对象是否是某种类型,使用if(obj is Class1)而不是强制转换它

我认为你应该首先尝试这样做,而不是强制转换。您可以为每个Classn定义一个接口并显式地实现这个接口。我认为Classn类不是第三方的,你可以修改它们。

public interface IIdProvider {
    int? Id { get; }
}
public class Class1 : IIdProvider {
    int? IIdProvider.Id { 
        get {
            return SomeProperty != null ? SomeProperty.Id : null;
        }
    }
}
public class Class2 : IIdProvider {
   int? IIdProvider.Id { 
        get {
            return AnotherProperty != null ? AnotherProperty.AnotherId : null;
        }
    }
}

有了这个,SomeMethod不关心Id来自哪里。如果你想添加Class5, Class6等,你不需要修改SomeMethod。相反,每个新添加的类将自己处理Id的事情。有了这个,我们可以做如下的事情。

public void SomeMethod(IIdProvider obj)
{
    AnotherMethod(obj.Id);
}

在这种特殊情况下(获取变量的单个值),使用类似开关的方法可能更有效,例如:

public int? GetSomeId(object obj) {
    Class1 class1 = obj as Class1;
    if (class1 != null)
    {
        return class1.SomeProperty != null ? class1.SomeProperty.Id : (int?)null;
    }
    Class2 class2 = obj as Class2;
    if (class2 != null)
    {
        return class2.AnotherProperty != null ? class2.AnotherProperty.AnotherId : (int?)null;
    }
    ....
    return null;
}

as关键字的存在是为了程序员的简洁。它被翻译成

Class1 class1 = obj is Class1 ? (Class1)obj : null;

我认为去as,因为它看起来更可读,也不影响性能。

我将使用:

if (obj is Class1 && ((Class1)obj).SomeProperty)
    someId = ((Class1)obj).SomeProperty.Id;

最终代码看起来像这样:

if (obj is Class1 && ((Class1)obj).SomeProperty)
    someId = ((Class1)obj).SomeProperty.Id;
else if (obj is Class2 && ((Class2)obj).AnotherProperty)
    someId = ((Class2)obj).AnotherProperty.Id;
else if (obj is Class3 && ((Class3)obj).OnceAgainAnotherProperty)
    ...

在每个ifelse if条件中,如果obj is X的第一个检查失败,则不会执行第二个((X)obj).XProperty。因此,在一天结束时,只会执行两次强制转换,即使您的对象类型为Class4 .