Что такое Type.GUID и как он связан с Type.Equals ()? - PullRequest
11 голосов
/ 02 декабря 2011

Я столкнулся с некоторым интересным поведением, пытаясь сравнить экземпляр System.RuntimeType с универсальным типом TOut:

Type runtimeT = methodInfo.ReturnType; // get RuntimeType using reflection
Type genericT = typeof(TOut);

// This condition fails because runtimeT doesn't 
// seem to include an assembly qualified name
if(runtimeT.Equals(genericT)) { ... }

Вот мои доказательства:

screenshot of debug watch window - Equals() returns false on types, true on GUIDs

Отказ от ответственности: Я не знаю точно, что такое GUID в контексте CLR / системы типов, за исключением того, что аббревиатура обозначает глобальный уникальный идентификатор .Возможно, имя вводит меня в заблуждение.

Предположение: Здесь я предполагаю, что Type GUID однозначно идентифицирует полностью определенный тип, включая AssemblyQualifiedNameэтого не хватает в factoryInfo.ReturnType на скриншоте (значение null).

Мое предположение неверно?

  • Да: Что делаетТип GUID действительно представляет, и как он используется?

  • Нет: Почему бы Equals() не было бы реализовано путем сравнения GUID?

Ответы [ 4 ]

22 голосов
/ 02 декабря 2011

Чтобы немного расширить ответ Джареда (полностью правильный):

В мире COM каждый интерфейс идентифицируется глобально уникальным идентификатором. Нет такой вещи как «изменение» интерфейса в COM; интерфейсы должны быть одинаковыми всегда. Вместо этого вы создаете новый интерфейс и назначаете ему новый GUID. Любые два интерфейса, которые отличаются, должны иметь разные GUID. Равенство интерфейса определено как равенство GUID в COM.

В мире .NET равенство типов сложнее. Тип связан с определенной сборкой, с одной стороны. Но не только это! Если вы загрузите одну и ту же сборку дважды (скажем, один раз по ее имени сборки и один раз по расположению на диске) и запросите две сборки для «одного и того же» типа, вы получите два объекта типа разных , и они не сравнивать как равные, хотя очевидно, что они имеют одинаковый GUID.

Очевидно, что это главная отправная точка; .NET и COM глубоко несовместимы в этом отношении. Что происходит, когда должно произойти взаимодействие? Каким-то образом COM и .NET должны договориться о некоторых правилах сравнения типов на равенство, когда оба используются в одном и том же процессе. (Потому что .NET вызывает код COM или наоборот.)

Таким образом, в .NET вы можете сказать: «этот тип связан с этим GUID». Когда применяются правила COM, код COM сравнивает два типа на равенство путем сравнения идентификаторов GUID, потому что именно это означает равенство в мире COM.

В .NET типы сравниваются на равенство с использованием обычных правил .NET.

Это представляет значительную потенциальную проблему в обычном сценарии. Предположим, вы написали программу .NET, которая взаимодействует с большой и сложной библиотекой COM. Просто чтобы выбрать совершенно неслучайный пример, предположим, что вы написали управляемое расширение для Word, которое имеет абсолютно огромную «поверхность» COM. Эта поверхность открывается для мира .NET через Первичную сборку взаимодействия, которая содержит «фиктивные» типы, которые имеют все те же GUID, что и соответствующие интерфейсы в мире COM. Затем можно написать код .NET для взаимодействия с уровнем COM через «фиктивные» объекты, которые выглядят как объекты COM соответствующего типа интерфейса, а код .NET - объектами соответствующего типа .NET.

Так что это прекрасно работает. А затем вы отправляете свою библиотеку .NET клиентам и понимаете, что Word не отправляет PIA клиентам автоматически . Скорее, вы обязаны отправить свою PIA. Что огромно.

Так родилась функция «без PIA» в C # 4. В C # 4 вы можете генерировать расширение Word, которое делает внутреннюю копию только части слова PIA, которое оно фактически использует . Который обычно намного меньше. Затем вы можете перераспределить вашу библиотеку расширений без перераспределения большой PIA.

Но это сразу же создает проблему. Предположим, что есть две таких библиотек, и они хотят взаимодействовать друг с другом , используя типы интерфейсов, которые являются общими для обоих, в PIA ? С точки зрения .NET типы создаются для каждой сборки; копии типов PIA в каждой библиотеке одинаковы с точки зрения COM, но отличаются с точки зрения .NET.

Поэтому мы добавили специальную функцию в CLR v4. В этой ситуации два типа в двух разных сборках (и тип PIA, если он есть!) унифицированы CLR и обрабатываются как равные GUID, совпадая поведение COM.

Детали, конечно, намного сложнее, чем этот простой набросок. Я просто хочу сказать, что вы открываете здесь огромную банку с червями; как GUID взаимодействуют с равенством типов - это глубокая и сложная тема, которую мало кто понимает полностью.

13 голосов
/ 02 декабря 2011

Одной из причин, по которой Type.GUID не используется в качестве сравнения для Equals, является то, что это контролируемый пользователем элемент.Например, я могу продиктовать GUID моего interface, выполнив следующее

[Guid("2bfd006d-94b9-43af-843f-5b32f7567086")]
interface IFoo { ... }

Это необходимо для создания интерфейсов для взаимодействия COM.Если Equals полагается на то, что GUID является уникальным для типа, то данный разработчик может нанести ущерб системе, дав своему типу тот же GUID, как, скажем, string, int и т. Д. *

2 голосов
/ 02 декабря 2011

Вы не должны полагаться на свойство GUID System.Type для сравнения типов. Особенно в межпроцессном взаимодействии (WCF, Remoting), Guid может не совпадать (если он не указан вручную, как в примере JaredPar).

Внутренне, Type.Equals использует RuntimeTypeHandle для сравнения на равенство.

Я не уверен, почему ваше сравнение на равенство не удается. Это не должно В приведенном ниже простом примере равные возвраты true.

    static void Main(string[] args)
    {
        Test<object>();
    }

    static object Test<T>()
    {
        var currentMethod = ((MethodInfo) MethodBase.GetCurrentMethod());
        var oType = currentMethod.ReturnType;
        var genericType = typeof (T);
        var equal = oType.Equals(genericType); // result is true.
        return null;
    }

Мой случайный выстрел в мрачном предположении - вы используете Entity Framework с включенным Proxy Creation Enabled? В этом случае универсальный параметр T IEntitySet является динамически генерируемым типом, который Entity Framework создал для вас ... Если это так, вы должны получить желаемый результат, сравнивая универсальные аргументы по отдельности:

0 голосов
/ 02 декабря 2011

GUID - это случайно сгенерированная последовательность байтов (16 по умолчанию), которая почти гарантированно никогда не повторится - ни по компьютерам, ни по времени.Полу-гарантированный, потому что возможность повторения существует, но она настолько крошечная, что не рассматривается.

Из-за этого большинство программистов используют GUID, когда им нужно предоставить идентификатор для чего-то, где они беспокоятся о наличии идентификатора.столкновение с другим идентификатором, который может в конечном итоге существовать в той же области (идентификаторы запущенных программ, или строк таблицы, или миллиона других вещей)

Но вкратце, вы можете думать о GUID как о случайном прерываниичисло, представляющее идентификатор, имеет длину 16 байтов и никогда не будет сгенерировано дважды.

...