Неявное / явное преобразование по отношению к ключевому слову «как»

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

Чтобы обойти это, я пытаюсь создать «фиктивную» версию этого UserIdentity, которую можно подключить к более жестко контролируемой переменной среде.

Короче говоря, у нас есть класс UserIdentity с несколькими общедоступными свойствами только для чтения и статическим CurrentIdentity (IIdentity) заполнитель. Я могу обойти почти все с "насмешкой"IIdentity реализации, но я наталкиваюсь на стену, когда достигаю точки, где CurrentIdentity приводится какUserIdentity.

Это довольно простой метод:

internal static UserIdentity GetCurrentIdentity()
{
    UserIdentity currentIdentity = ApplicationContext.User.Identity as UserIdentity;
    return currentIdentity;
}

Я создал свой фиктивный объект, чтобы создать членаUserIdentity введите, а затем сделайте что-то вроде этого:

    public static implicit operator UserIdentity(MockUserIdentity src)
    {
        return src.UserIdentity;
    }

или это

    public static explicit operator UserIdentity(MockUserIdentity src)
    {
        return src.UserIdentity;
    }

Проблема в том, что, насколько я могу судить, это «как» не вызывает ни явной, ни явной операции преобразования моего фиктивного объекта. Мой (ые) вопрос (и): я пропускаю что-то простое здесь или это не сработает, потому что (я предполагаю) операция 'as' смотрит прямо на наследование классов (что мой объект не делает ...) ?

Кроме того, может быть, немного не по теме, но почему в классе не может быть одновременных явных и неявных операторов одного и того же результирующего типа? Если я не пропустил что-то глупое, компилятор не работает, если я пытаюсь использовать оба оператора преобразования одновременно. Я должен выбрать один или другой.

ОБНОВИТЬ

Хорошо, теперь я полностью сбит с толку. Может быть, я становлюсь неаккуратным, но я попытался сделать прямое приведение, и я не могу заставить это работать. Я прочитал об операторе в MSDN, и в примере показан оператор, идущий в результирующий класс, а не в исходный класс, но я не уверен, имеет ли это значение или нет (я пробовал оба места в коде ниже). В любом случае, я пытался установить простой тестовый стенд, чтобы увидеть, что я могу делать неправильно, но я тоже не могу заставить это работать ... Вот что у меня есть

class Program
{
    // Shared Interface
    public interface IIdentity { }

    // "real" class (not conducive to inheritence)
    public class CoreIdentity : IIdentity
    {
        internal CoreIdentity() { }

        // Just in case (if this has to be here, that seems unfortunate)
        public static explicit operator CoreIdentity(ExtendedIdentity src)
        {
            return src.Identity;
        }
    }

    // "mock" class (Wraps core object)
    public class ExtendedIdentity : IIdentity
    {
        public CoreIdentity Identity { get; set; }
        public ExtendedIdentity()
        {
            Identity = new CoreIdentity();
        }

        // This is where the operator seems like it should belong...
        public static explicit operator CoreIdentity(ExtendedIdentity src)
        {
            return src.Identity;
        }
    }

    // Dummy class to obtain "current core identity"
    public class Foo
    {
        public IIdentity Identity { get; set; }
        public CoreIdentity GetCoreIdentity()
        {
            return (CoreIdentity)Identity;
        }
    }

    static void Main(string[] args)
    {
        ExtendedIdentity identity = new ExtendedIdentity();
        Foo foo = new Foo();
        foo.Identity = identity;
        CoreIdentity core = foo.GetCoreIdentity();
    }
}

Но это вызывает следующее исключение, когда я вызываю foo.GetCoreIdentity ():

Невозможно привести объект типа «ExtendedIdentity» к типу «CoreIdentity».

и я не могу поймать ни один из моих явных операторов с точкой останова, так что похоже, что он делает это определение, даже не «пробуя» маршруты конвертации, которые я указал.

Конечно, я упускаю что-то очевидное. Помогает ли тот факт, что мой Identity (в Foo), определенный как IIdentity, как-то мешает разрешению приведения с использованием явных операторов реализующего типа? Это показалось бы мне странным.

ОБНОВЛЕНИЕ (# 2)

У меня такое чувство, что я спамую в своем сообщении со всеми этими обновлениями (возможно, я должен собрать свои действия, прежде чем быть настолько счастливым для триггера :)) В любом случае, я изменил метод GetCoreIdentityMethod моего Foo, чтобы сделать это вместо этого:

public CoreIdentity GetCoreIdentity()
{
    ExtendedIdentity exId = Identity as ExtendedIdentity;
    if (exId != null)
        return (CoreIdentity)exId;

    return (CoreIdentity)Identity;
}

и (после очистки неоднозначной ссылки, вызванной наличием оператора в обоих классах), он вошел в мой явный код оператора преобразования и работал должным образом. Поэтому я думаю, что это выглядит так, как будто явные операторы не разрешаются полиморфно (это правильное понимание?), И тот факт, что мое свойство было напечатано как IIdentity, а не как ExtendedIdentity, препятствовало тому, чтобы оно вызывало логику приведения, даже если это было Тип ExtendedIdentity во время его вызова. Это кажется мне очень странным и неожиданным ... и довольно неудачным.

Я не хочу переписывать хранитель объекта CurrentIdentity, чтобы он знал о моих специальных тестовых приведениях. Я хотел включить эту «особую» логику в сам макет, так что это действительно заставляет меня задуматься.

Ответы на вопрос(4)

Помогает ли тот факт, что мой Identity (в Foo), определенный как IIdentity, как-то мешает разрешению приведения с использованием явных операторов реализующего типа?

Вот подсказка: как вы определяете явный (или неявный, в этом отношении) оператор преобразования? (Я знаю, что вы знаете это, так как вы уже сделали это; я задаю вопрос, чтобы проиллюстрировать это.)

public static explicit operator UserIdentity(MockUserIdentity src)
{
    return src.UserIdentity;
}

Здесь есть что-то очень важное, что нужно осознать. Дизайнеры C # сделали правильный выбор, сделав всех операторовстатический, Таким образом, явный оператор, определенный выше, переводит в сущности статический вызов метода, который выглядит примерно так:

public static UserIdentity op_Explicit(MockUserIdentity src)
{
    return src.UserIdentity;
}

Теперь вот к чему я клоню. Поведение, которое озадачило вас в вашем вопросе, потому что этоказалось сбой в отделе полиморфизма действительно был результатом системы разрешения перегрузки методов в C #.

Если у меня есть два метода:

void Write(string s) { Console.WriteLine("string"); }
void Write(object o) { Console.WriteLine("object"); }

... а потом у меня есть эта программа:

object x = "Hello!";
Write(x);

Каким будет выход?

Ответ«Объект» посколькуWrite(object) Перегрузка была выбрана компилятором - так и должно было быть.Write не является экземпляром метода, который должен быть переопределен некоторым производным типом в соответствии с обычным полиморфизмом; это статический метод с перегрузками, между которыми компилятор должен сделать выбор. посколькуx в приведенном выше коде объявлен типаobject, этот выбор однозначенWrite(object).

Так что в случае вашего кода, где у вас есть это:

public IIdentity Identity { get; set; }
public CoreIdentity GetCoreIdentity()
{
    return (CoreIdentity)Identity;
}

Компилятор должен исследовать: есть лиop_Explicit перегрузка, которая принимаетIIdentity параметр?Нет, нет Есть тот, который принимаетUserIdentity параметр, но это слишком конкретно (так же, какWrite(string) был слишком конкретным дляx в приведенном выше примере).

Поэтому причина, по которой ваш явный оператор не был вызван в ваших первоначальных тестах, заключалась в том, что компилятор не разрешал(CoreIdentity)Identity к этой конкретной перегрузке. Это также, почему ваша модифицированная версияделает Работа:

public CoreIdentity GetCoreIdentity()
{
    ExtendedIdentity exId = Identity as ExtendedIdentity;

    if (exId != null)
    {
        // Since exId is actually declared to be of type ExtendedIdentity,
        // the compiler can choose the operator overload accepting
        // an ExtendedIdentity parameter -- so this will work.
        return (CoreIdentity)exId;
    }

    return (CoreIdentity)Identity;
}
 Steven06 окт. 2010 г., 20:15
Спасибо за это! Я думаю, что я начинаю, потому что вижу, что части начинают обнаруживаться, но ваш пост объяснил все это довольно хорошо.

Как упомянуто Рэемas не вызывает операторы преобразования.

Тем не менее, вы должны использовать явное приведение в таких сценариях.

Таким образом, вы получите очень четкую информацию, когда что-то настроено неправильно, и объект в ApplicationContext.User.Identity не соответствует ожидаемому коду.

 eglasius06 окт. 2010 г., 20:07
это хоть один, об этом не думал. Это верно, на самом деле, он не увидит его, если не получит доступ к типу MockUserIdentity. Я не уверен, что неявные операторы делают то же самое. Если нет, то, возможно, вам придется использовать mock для наследования UserIdentity и переопределять то, что вам нужно.
 Steven06 окт. 2010 г., 19:52
Спасибо! Проблема, с которой я сейчас сталкиваюсь, заключается в том, что если я иду по пути явного приведения, базовый тип переменной-члена (IIdentity) находится не там, где определена моя явная операция приведения, а поскольку этот оператор является статическим, он не кажется полиморфным в том смысле, что когда мой член IIdentity установлен на ссылку на мой фиктивный объект, он «не знает», что нужно пройти через операцию фиктивного класса.

Итак, почему бы вам не использовать явное приведение?

// will throw if cast fails
internal static UserIdentity GetCurrentIdentity()
{
    UserIdentity currentIdentity = (UserIdentity) ApplicationContext.User.Identity ;
    return currentIdentity;
}

Это должно вызвать ваш явный оператор. Вы можете проверить сis во-первых, чтобы сделать его более безопасным.

 Steven06 окт. 2010 г., 17:25
Я думаю, что тест «is» попадает в ту же ловушку, что и «делает». Так как мой мой фиктивный объект не наследуется от UserIdentity, я думаю, что тест "is" также провалится. Явное приведение без "is" является хорошим предложением. Спасибо!
Решение Вопроса

as не вызывает операторы преобразования. Увидеть:http://msdn.microsoft.com/en-us/library/cscsdfbt(v=VS.100).aspx

Используйте (приведение).

 Steven06 окт. 2010 г., 17:26
Это отвечает на мой вопрос, но это было своего рода то, чего я боялся. Спасибо!
 Ray Henry07 окт. 2010 г., 18:47
@ Steven, основываясь на обновлении, которое вы сделали для своего вопроса, есть другой способ сделать это, если ваш класс и макет используют общий интерфейс, и вы можете изменить этот интерфейс. Это не красиво, но может работать в вашем случае. Добавьте IIdentity AsCoreIdentity () к интерфейсу. Пусть CoreIdentity вернет «this», а Mock вернет переменную Identity экземпляра.

Ваш ответ на вопрос