#c# #reflection #casting #invoke #primitive-types
#c# #универсальные
Вопрос:
Предположим, у нас есть интерфейс с одним универсальным методом:
public interface IExtender
{
T GetValue<T>(string tag);
}
и простая его реализация, которая возвращает экземпляры двух разных типов (B и C) в зависимости от параметра «tag»:
public class A : IExtender
{
public T GetValue<T>(string tag)
{
if (typeof(T) == typeof(B) amp;amp; tag == null)
return (T)(object) new B();
if (typeof(T) == typeof(C) amp;amp; tag == "foo")
return (T)(object) new C();
return default(T);
}
}
можно ли избежать двойного приведения (T)(object)
? Или, есть ли способ сообщить компилятору «эй, я уверен, что это приведение не завершится ошибкой во время выполнения, просто позвольте мне сделать это без предварительного приведения к объекту!»
Комментарии:
1. Зачем вам нужно преобразование (T) (Object)? Вы можете напрямую (T) создать C(), верно?
2. @Anuraj: Нет — в этом весь смысл вопроса. Пожалуйста, прочтите сообщение в блоге, на которое ссылается мой ответ.
Ответ №1:
Или, есть ли способ сообщить компилятору «эй, я уверен, что это приведение не завершится ошибкой во время выполнения, просто позвольте мне сделать это без предварительного приведения к объекту!»
Нет, язык специально разработан для предотвращения этого. Эрик Липперт недавно написал об этом в блоге. Я согласен, что это раздражает, но в этом есть определенный смысл.
Честно говоря, «общие» методы, подобные этому, обычно немного пахнут дизайном. Если метод должен иметь особые случаи для разных типов, вам следует, по крайней мере, рассмотреть возможность использования отдельных методов вместо этого. ( GetB
, GetC
)
Комментарии:
1. Ага. getB, GetC и GetEtc здесь не подходят, но я не знаю, подходящее ли это место для подробного обсуждения дизайна. В любом случае, первый абзац и ссылка — это ответ, который я искал. Спасибо.
2. Да, это возможно более чем одним способом, и иногда это единственное хорошее решение проблемы проектирования. Именно поэтому было введено ключевое слово dynamic. И преобразование. Тип изменения.
3. @DinoDini: Я не думаю, что кто-то из них на самом деле делает то, о чем говорит ОП. OP не хочет выполнять какое-либо фактическое преобразование значений — они просто хотят избежать двойного приведения. Эффективное использование dynamic просто делает преобразование
object
неявным, но не удаляет его каким-либо полезным способом.4. Ссылка на блог пропала, но я нашел ее здесь ericlippert.com/2012/07/10/696
5. @LouiseEggleton: Спасибо, я его сейчас отредактировал. Для будущих подобных ситуаций, не стесняйтесь предлагать редактирование напрямую.
Ответ №2:
public T MyMethod<T>(string tag) where T : class
{
return new A() as T;
}
Комментарии:
1. именно то, что я искал!
Ответ №3:
проверьте этот пример:
public T GetValue<T>(string tag) where T : class, new()
{
if (typeof(T) == typeof(B) amp;amp; tag == null)
return new T();
if (typeof(T) == typeof(C) amp;amp; tag == "foo")
return new T();
return default(T);
}
приведение не требуется, вы можете создать экземпляр «T», просто добавьте общее ограничение, в котором говорится, что T является классом и имеет конструктор без параметров, поэтому вам не нужно создавать другие базовые типы, и вы можете быть уверены, что только подходящие типы будут проходить через этот общий метод.
Комментарии:
1. Ну, это то, что происходит, когда вы (я, на самом деле) пытаетесь упростить код для публикации в качестве примера. В реальном коде экземпляры уже существуют, и я не могу наложить никаких ограничений на T. Так что да, это работает (спасибо!), Но не решает мою проблему.
Ответ №4:
Вы можете использовать dynamic для хранения вашего реального результата, но вы должны быть уверены, что общий тип аргумента является правильным типом, который вы возвращаете.
TResult GetResult<TResult>()
{
dynamic r = 10;
return r;
}
Комментарии:
1. Фактический ответ. Внизу страницы. Снова.
2. Это на самом деле не удаляет приведение — оно просто делает его неявным, а не явным. Кроме того, он, скорее всего, будет работать гораздо хуже, чем двойной бросок.
3. Это действительно сработало как обходной путь для меня. Если программист сам проверяет тип, я не вижу проблемы.
Ответ №5:
Нет, это невозможно. Единственный способ сделать это — сообщить компилятору о дополнительных допущениях T
. Как видно из списка общих ограничений параметров, в C # не определено никаких ограничений, требующих наличия определенного приведения.
Комментарии:
1. Это возможно при динамическом приведении.
Ответ №6:
если вы позволите B и C реализовать один и тот же интерфейс, вы могли бы использовать ограничение типа для вашего T
. Возможно, это не совсем то, чего вы хотите, но, как предполагают и другие, то, чего вы хотите, на самом деле невозможно.
public interface IclassBndC {}
public class B : IclassBandC {}
public class C : IclassBandC {}
public class A : IExtender
{
public T GetValue<T>(string tag) where T : IclassBandC
{
if (tag == null)
return new B();
if (tag == "foo")
return new C();
return default(T);
}
}
Комментарии:
1. Обратите внимание , что для этого требуется , чтобы вы могли изменить
B
иC
.2. @O.R.Mapper верно, я пропустил упоминание о том, что это невозможно?
3. Вы этого не сделали, но там не было упоминания об этом
B
, иC
они написаны OP, а не предопределены и запечатаны. Просто подумал, что следует упомянуть об ограничении.