【问题标题】:Casting to a Tuple<object,object>转换为元组<object,object>
【发布时间】:2014-04-08 09:04:52
【问题描述】:

考虑这段代码,我正在测试一个未知变量,它可能是 int、MyObj 或 Tuple,我正在做一些类型检查以查看它是什么,以便我可以继续处理数据取决于它是什么:

class MyObj { }

// ...

void MyMethod(object data) {

    if (data is int) Console.Write("Datatype = int");
    else if (data is MyObj) Console.Write("Datatype = MyObj");
    else if (data is Tuple<object,object>) {

        var myTuple = data as Tuple<object,object>;
        if (myTuple.Item1 is int && myTuple.Item2 is int) Console.WriteLine("Datatype = Tuple<int,int>");
        else if (myTuple.Item1 is int && myTuple.Item2 is MyObj) Console.WriteLine("Datatype = Tuple<int,MyObj>");

        // other type checks            
    }
}

// ...

MyMethod(1);                            // Works : Datatype = int
MyMethod(new MyObj());                  // Works : Datatype = MyObj
MyMethod(Tuple.Create(1, 2));           // Fails
MyMethod(Tuple.Create(1, new MyObj());  // Fails

// and also...

var items = new List<object>() {
    1,
    new MyObj(),
    Tuple.Create(1, 2),
    Tuple.Create(1, new MyObj())
};
foreach (var o in items) MyMethod(o);

我的问题是 Tuple&lt;int,MyObj&gt; 不能转换为 Tuple&lt;object,object&gt;,但我可以单独将 int 转换为 objectMyObj 转换为 object

如何进行演员表?

【问题讨论】:

    标签: c# generics casting tuples


    【解决方案1】:

    如果要检查任何对类型,则必须使用反射:

    Type t = data.GetType();
    if(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Tuple<,>))
    {
        var types = t.GetGenericArguments();
        Console.WriteLine("Datatype = Tuple<{0}, {1}>", types[0].Name, types[1].Name)
    }
    

    您也许可以使用重载和动态而不是手动检查类型:

    MyMethod(MyObject obj) { ... }
    MyMethod(int i) { ... }
    MyMethod(Tuple<int, int> t) { ... }
    MyMethod(Tuple<int, MyObject> t) { ... }
    
    foreach(dynamic d in items)
    {
        MyMethod(d);
    }
    

    这将在运行时选择最佳重载,因此您可以直接访问元组类型。

    【讨论】:

    • 感谢您的答案修订:) 我希望有一种不需要反思的方法,但我想没有。
    • @BG100 +1 据我所知,别无选择。
    • 感谢您的帮助。我刚刚在我的代码中实现了这一点,它运行良好。我还使用:var itemX = t.GetProperty("ItemX").GetValue(data); 来获取实际数据。
    • @BG100 - 好的,太好了。我添加了一种使用动态方式使 Jodrell 的答案起作用的方法。
    【解决方案2】:

    如您所见,here, Tuple&lt;T1, T2&gt; 不是协变的。

    与其尝试创建一种可以接受任何类型参数的方法,不如重载您的函数以接收您实际期望的有效类型。

    也许,

    void MyMethod<T1,T2>(Tuple<T1, T2> data)
    {
        // In case ToString() is overridden
        Console.WriteLine("Datatype = Tuple<{0}, {1}>",
            typeof(T1).Name,
            typeof(T2).Name);
    }
    
    void MyMethod(MyObj data)
    {
        Console.WriteLine("Datatype = MyObj");
    }
    
    void MyMethod(int data)
    {
        Console.WriteLine("Datatype = System.Int32");
    }
    

    这种方式不需要类型检查,编译器会在编译时为你做。这不是 javascript,强类型可能是一个好处。

    【讨论】:

    • 为了简洁起见,我把它省略了,但我还想做的是:foreach (var o in myListOfObjects) MyMethod(o); 所以我在编译时不知道数据类型是什么......
    • @BG100,那么,myListOfObjects 是什么,它来自哪里。我认为这可能会引发关于泛型集合的问题。
    • 它来自哪里重要吗?我可以解释,但代码示例需要很长时间......我只是想知道一种方法来检查可能是 Tuple&lt;??,??&gt; 的未知 object 的类型。
    【解决方案3】:

    在您的代码中,您明确地检查intMyObj,以及它们的位置(Item1Item2),如下所示:

        if (myTuple.Item1 is int && myTuple.Item2 is int)
        else if (myTuple.Item1 is int && myTuple.Item2 is MyObj)
    

    您可以使用您已经编写的相同框架进行相同的显式检查:

        if (data is int) Console.WriteLine("Datatype = int");
        else if (data is MyObj) Console.WriteLine("Datatype = MyObj");
        else if (data is Tuple<int, int>) Console.WriteLine("Datatype = Tuple<int,int>");
        else if (data is Tuple<int, MyObj>) Console.WriteLine("Datatype = Tuple<int,MyObj>");
    

    上述代码的缺点与您尝试使用 Tuple&lt;object, object&gt; 所做的相同:您必须明确检查可能进入元组的所有组合及其位置,即我的代码不会如果您传入Tuple&lt;double, double&gt;Tuple&lt;MyObj, int&gt;,则按书面规定工作,但如果您的代码实际上按您希望的方式工作,您的代码也不会。如果您不想要显式/硬编码的值,那么您似乎必须使用 Lee 的回答中所见的反射,否则上面的代码可以用更少的工作完成您想要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      • 1970-01-01
      • 2015-08-14
      • 2016-04-14
      • 2014-05-09
      • 2012-06-03
      • 1970-01-01
      相关资源
      最近更新 更多