首页 > 解决方案 > 一个对象可以转换为两种不相关的类型吗?

问题描述

我有一种情况,我收到的对象只能是两种已知类型之一——我们称它们为“typeA”和“typeB”。这两种类型互不相关。

我需要先找到正确的类型才能使用该对象。这是一些代码,显示了我最初尝试找到正确类型的尝试:

    public void OneOfTwoTypes(object obj)
    {
        try 
        { 
            var objType = (typeA)obj; 
        }
        catch (InvalidCastException)
        {
            var objType = (typeB)obj; 
        }

        // extra code for doing things to this object. 

    }

由于作用域,上面的代码不会运行。而且我也不能将 var 声明为 null 。所以我改用动态:

    public void OneOfTwoTypes(object obj)
    {
        dynamic objType;
        try 
        { 
            objType = (typeA)obj; 
        }
        catch (InvalidCastException)
        {
            objType = (typeB)obj; 
        }

        // extra code for doing things to this object. 

    }

这运行良好并且可以完成工作。我的问题是 - 有没有更好的方法来做到这一点?另外,之前没有使用过动态,我当前的实现是否有任何重大问题?

编辑:围绕对象的用途添加一些评论。typeA 和 typeB 对象实现相似的属性,但代表不同的物理实体(一个代表施加的压力,另一个代表点载荷)。对于上面的代码,我将使用非常相似的属性(例如 typeA.propertyA 表示来自雪的压力载荷,并且在概念上与 typeB.propertyA 相似,因为两者都来自雪载荷,但后者指的是点载荷。请注意,两者确实有一些不同的属性)。

最终输出将是负载组合的计算 - 无论使用 typeA 还是 typeB ,都将包括相同类型的计算。

我不想使用方法重载,因为这将涉及对两种对象类型的相同代码的大量复制(我们在这里谈论数百行)。我确实喜欢两种类型都实现的接口的想法。

标签: c#

解决方案


首先,c# 并不是真正构建为将对象作为对象传递的。强类型语言的最大好处之一是当我想要一个列表时获取一个数组对象并没有任何意外,反之亦然。您的第一个选择应该是一个共享接口,两者都可以实现,您可以作为接口而不是显式实现传递。

不过,有时这并不总是有效。在这种情况下,您仍然可以选择。

像 c# 这样的语言在运行时也知道对象类型。基本 api 是obj.GetType() == typeof(TypeA),但仅在类型相同且不考虑继承关系或接口实现时才评估为 true 。起点将是,如果可以为对象分配类型,obj is TypeA则返回,否则返回。这也可以在 if 语句中使用(见下文)。trueTypeAfalse

如果您需要测试不止一种或两种类型,还可以使用 C# 8 中的模式匹配开关。

// we'll pretend for this example that array and list of 2 completely different objects that need to be handled in code in completely different ways
var obj = new Random().Next(2) == 1
    ? (IEnumerable<Guid>) new []{ Guid.NewGuid() }
    : new List<Guid> { Guid.NewGuid() };

// option 1: if statements with `is` keyword
if (obj is List<Guid> list)
{
    // the variable list in in scope in the if block, since it was declared in the if statement
    HandleList(list);
}
else if (obj is Guid[] array)
{
    // the variable array is in scope in the else block
    HandleArray(array);
}


// option 2: pattern matching switch
switch (obj) 
{
    case List<Guid> list:
        HandleList(list);
        break;
    case Guid[] array:
        HandleArray(array);
        break;
    default:
        throw new ArgumentOutOfRangeException();
}

推荐阅读