通用方法如何知道无法访问的类型? (或“我如何失去一美元”)

时间:2011-04-16 12:55:43

标签: c# generics casting

所以,我们对我们的团队进行了一些赌注,看看是否有效。我输了。这是代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using MyNamespace;

namespace PrivateGeneric
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void WhoIsRight_Click(object sender, RoutedEventArgs e)
        {
            var store = new GenericDataStore();

            try
            {
                var data = new MyPrivateClass();
                store.StoreTheData(data);
                object theData = store.GetTheData<MyPrivateClass>();
                if (theData == null || !(theData is MyPrivateClass))
                {
                    throw new Exception();
                }
                MessageBox.Show("Seann was right.");
            }
            catch (Exception)
            {
                MessageBox.Show("WOOOOOOOOOT!!!!! PHIL WINS!!!!!! HAHAHAHA!!!!!! PWNED!!!!!!!");
            }
        }

        private class MyPrivateClass
        {

        }
    }
}

namespace MyNamespace
{
    public class GenericDataStore
    {
        readonly List<object> _store = new List<object>();

        public void StoreTheData<T>(T data)
        {
            _store.Add(data);
        }

        public object GetTheData<T>()
        {
            //How does "t is T" work if T is a private type unknown to this class?
            return _store.FirstOrDefault(t => (t is T));
        }
    }
}

代码中突出显示了其工作原理的问题。 “是”是否需要转换为T以确定它是否实际上是T?它不需要这种类型可访问吗?显然情况并非如此,所以突出显示的行使用什么机制来确定它?

3 个答案:

答案 0 :(得分:9)

以这种方式思考:

class C
{
    private const int BigSecret = 0x0BADFOOD;
    public static void M() { D.X(BigSecret); }
}
class D
{
    public static void X(int y) { Console.WriteLine(y); }
}

当C.M被召唤时,D.X了解这个秘密并与世界分享。 BigSecret是私有的这一事实无关紧要;你传递了。不允许代码查看私有字段的,不允许使用私有字段的名称。 X完全可以自由地使用y的值,因为它认为合适;它没有做任何违法行为,比如试图使用BigSecret的名称

类型参数的方式相同。它们是逻辑上参数传递给泛型函数。当然它们不是使用相同的机制传递它们,但逻辑上它们只是传递的参数设置了类型参数的值,就像常规参数设置形式参数的值一样。如果您不想传递秘密私有类型,则不会将其作为类型参数传递

在您的情况下,StoreTheData不允许使用PrivateClass的名称,但如果传递了PrivateClass的,它可以使用它想要的一切。如果您不想使用它,那么您不应该通过它。就像你不想知道BigSecret一样,那么你不应该传递它。秘密共享不再是秘密。

顺便提一下,我们利用泛型类型不对其类型参数进行可访问性检查以使匿名类型工作的事实。有关详细信息,请参阅我关于该主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2010/12/20/why-are-anonymous-types-generic.aspx

答案 1 :(得分:5)

我认为你混淆了两个独立的概念 - 可见性和运行时类型信息。

每个struct / class都有类型信息。如何获得该类型信息并不重要。例如。你可以反思一个程序集并挖出一些私有类型,GetTheData<T>方法仍然可以按预期运行。

在这种情况下,您传入的MyPrivateClass类型在调用网站上可见(因为MyPrivateClass嵌套在您的主类中,因此可以看到它)。因此,您知道呼叫站点的类型,并将其传递给GetTheData<T>方法。

此时,类型的信息是已知的,并作为泛型类型参数传递。然后,您的代码使用该泛型类型参数进行类型检查。

这样做:

GetTheData<T>()
{
    Console.WriteLine("Type information: {0}", typeof(T).FullName); 
}

如果使用类型MyPrivateClass调用此方法 - 它将按预期打印类型的名称。

*已编辑 - 删除了有关Type比较的内容,因为Eric L指出is / as运算符有一条IL指令,我不确定除此之外会发生什么。出于问题的目的,您需要知道的是运行时知道类型并且可以足够快地比较它们。

答案 2 :(得分:1)

您的私有类型在定义它的类中是可见的,因此没有范围问题。您还可以执行以下操作:

public interface ISomeInterface { }

public class SomeInterfaceFactory
{
   private class SomeClass : IAnInterface { }

   public ISomeInterface Create() { return new SomeClass(); }
}

现在外部创建的ISomeInterface实际上是SomeClass的一个实例,即使该类对SomeInterfaceFactory是私有的,您也可以通过反射进行验证。