Как сделать статическое приведение в C #? - PullRequest
3 голосов
/ 09 октября 2010

Учитывая пару типов, как это:

interface I {}
class C : I {}

Как я могу сделать приведение статического типа?Под этим я подразумеваю: как я могу изменить его тип так, чтобы его проверяли во время компиляции?

В C ++ вы можете сделать static_cast<I*>(c).В C # лучшее, что я могу сделать, это создать временную переменную альтернативного типа и попытаться присвоить ее:

var c = new C();
I i = c;  // statically checked

Но это мешает беглому программированию.Я должен создать новую переменную только для проверки типа.Итак, я остановился на чем-то вроде этого:

class C : I
{
    public I I { get { return this; } }
}

Теперь я могу статически конвертировать C в I, просто вызвав c.I.

Есть ли лучший способ сделать это в C #?

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

ОБНОВЛЕНИЕ

Другой вариант, который я придумал, - расширение объекта:

public static class ObjectExtensions
{
    [DebuggerStepThrough]
    public static T StaticTo<T>(this T o)
    {
        return o;
    }
}

Так что ((I)c).Doit() также может быть c.StaticTo<I>().Doit(),Хм ... наверное все равно будет придерживаться простого состава.Полагал, что я опубликую этот другой вариант в любом случае.

Ответы [ 4 ]

5 голосов
/ 09 октября 2010

Просто разыграйте:

(I)c

Редактировать пример:

var c = new C();

((I)c).MethodOnI();
2 голосов
/ 25 января 2016

Напишите метод расширения, который использует трюк, упомянутый вами в ОБНОВЛЕНИИ:

public static class ObjectExtensions
{
    public static T StaticCast<T>(this T o) => o;
}

Для использования:

things.StaticCast<IEnumerable>().GetEnumerator();

Если things, например, IEnumerable<object>,это компилируется.Если things равно object, произойдет сбой.

// Compiles (because IEnumerable<char> is known at compiletime
// to be IEnumerable too).
"adsf".StaticCast<IEnumerable>().GetEnumerator();

// error CS1929: 'object' does not contain a definition for 'StaticCast'
// and the best extension method overload
// 'ObjectExtensions.StaticCast<IEnumerable>(IEnumerable)'
// requires a receiver of type 'IEnumerable'
new object().StaticCast<IEnumerable>().GetEnumerator();

Зачем использовать статическое приведение?

Одной из распространенных практик при рефакторинге является продолжение и внесение изменений, а затем проверкаВаши изменения не вызвали каких-либо регрессов.Вы можете обнаружить регрессии различными способами и на разных этапах.Например, некоторые типы рефакторинга могут привести к изменениям / поломкам API и потребовать рефакторинга других частей кодовой базы.

Если одна часть вашего кода ожидает получить тип (ClassA), который должен быть известен приВремя компиляции для реализации интерфейса (IInterfaceA) и того, что код хочет получить непосредственный доступ к элементам интерфейса, может потребоваться привести к типу интерфейса, например, для доступа к явно реализованным элементам интерфейса.Если после рефакторинга ClassA больше не реализует IIterfaceA, вы получите различные типы ошибок в зависимости от того, как вы преобразовали интерфейс:

  1. приведение в стиле C: ((IInterfaceA)MethodReturningClassA()).Act(); внезапно станет приведением во время выполнения и выдаст ошибку времени выполнения.
  2. Присвоение переменной с явным типом: IInterfaceA a = MethodReturningClassA(); a.Act(); вызовет ошибку времени компиляции.
  3. Использование static_cast<T>-подобный метод расширения: MethodReturningClassA().StaticCast<IInterfaceA>().Act(); вызовет ошибку времени компиляции.

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

2 голосов
/ 09 октября 2010
var c = new C(); 
I i = c;  // statically checked

равно

I i = new C();
1 голос
/ 09 октября 2010

Если вы действительно ищете способ увидеть, реализует ли объект определенный тип, вам следует использовать as.

I i = whatever as i;
if (i == null) // It wasn't

В противном случае, вы просто разыгрываете его. (На самом деле не существует нескольких типов приведения в .NET, как в C ++ - если только вы не стали глубже, чем нужно большинству людей, но тогда речь пойдет о WeakReference и подобных вещах.)

I i = (I)c;

Если вы просто ищете удобный способ превратить что-либо, реализующее I в I, то вы можете использовать метод расширения или что-то подобное.

public static I ToI(this I @this)
{
    return @this;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...