У меня есть класс Animal
и его подкласс Dog
. Я часто пишу следующие строки:
if (animal is Dog)
{
Dog dog = animal as Dog;
dog.Name;
...
}
Для переменной Animal animal;
.
Есть ли какой-нибудь синтаксис, который позволяет мне написать что-то вроде:
if (Dog dog = animal as Dog)
{
dog.Name;
...
}
c#
casting
if-statement
Майкл
источник
источник
bool
состоянии?null
,! =false
В C #; C # допускает только фактические bools или вещи, неявно конвертируемые в bools вif
условиях. Ни значения NULL, ни какой-либо из целочисленных типов не могут быть неявно преобразованы в bools.Ответы:
Ответ ниже был написан много лет назад и со временем обновлялся. Начиная с C # 7, вы можете использовать сопоставление с образцом:
if (animal is Dog dog) { // Use dog here }
Обратите внимание, что
dog
это все еще находится в области действия послеif
оператора, но не определено.Нет, нет. Однако более идиоматично написать это:
Dog dog = animal as Dog; if (dog != null) { // Use dog }
Учитывая, что «как следует за условием» почти всегда используется таким образом, было бы целесообразнее использовать оператор, выполняющий обе части за один раз. В настоящее время этого нет в C # 6, но может быть частью C # 7, если реализовано предложение сопоставления с образцом .
Проблема в том, что вы не можете объявить переменную в условной части
if
оператора 1 . Самый близкий подход, который я могу придумать, таков:// EVIL EVIL EVIL. DO NOT USE. for (Dog dog = animal as Dog; dog != null; dog = null) { ... }
Это просто противно ... (Я только что попробовал, и это работает. Но, пожалуйста, не делайте этого. О, и вы, конечно, можете объявить
dog
использованиеvar
).Конечно, вы можете написать метод расширения:
public static void AsIf<T>(this object value, Action<T> action) where T : class { T t = value as T; if (t != null) { action(t); } }
Затем вызовите его с помощью:
animal.AsIf<Dog>(dog => { // Use dog in here });
В качестве альтернативы вы можете объединить два:
public static void AsIf<T>(this object value, Action<T> action) where T : class { // EVIL EVIL EVIL for (var t = value as T; t != null; t = null) { action(t); } }
Вы также можете использовать метод расширения без лямбда-выражения более чистым способом, чем цикл for:
public static IEnumerable<T> AsOrEmpty(this object value) { T t = value as T; if (t != null) { yield return t; } }
Затем:
foreach (Dog dog in animal.AsOrEmpty<Dog>()) { // use dog }
1 Вы можете присваивать значения в
if
операторах, хотя я редко это делаю. Однако это не то же самое, что объявление переменных. Это не очень необычно для меня , чтобы сделать это вwhile
хотя , когда чтение потоков данных. Например:string line; while ((line = reader.ReadLine()) != null) { ... }
В наши дни я обычно предпочитаю использовать оболочку, которая позволяет мне использовать,
foreach (string line in ...)
но я рассматриваю вышесказанное как довольно идиоматический шаблон. Это , как правило , не приятно иметь побочные эффекты в пределах состояния, но альтернативы , как правило , включают дублирование кода, и если вы знаете эту модель легко получить право.источник
EVIL EVIL EVIL
, но я не уверен.AsEither(...)
, я думаю, он немного понятнееAsIf(...)
, поэтому я могу писатьmyAnimal.AsEither(dog => dog.Woof(), cat => cat.Meeow(), unicorn => unicorn.ShitRainbows())
.В случае
as
неудачи возвращаетсяnull
.Dog dog = animal as Dog; if (dog != null) { // do stuff }
источник
if
оператора, а не во внешней области.if
может иметь результат типа bool и присваивание.Dog dog; if ((dog = animal as Dog) != null) { // Use Dog }
но это по-прежнему вводит переменную во внешнюю область видимости.Вы можете присвоить значение переменной, если она уже существует. Вы также можете ограничить переменную, чтобы это имя переменной можно было использовать позже в том же методе, если это является проблемой.
public void Test() { var animals = new Animal[] { new Dog(), new Duck() }; foreach (var animal in animals) { { // <-- scopes the existence of critter to this block Dog critter; if (null != (critter = animal as Dog)) { critter.Name = "Scopey"; // ... } } { Duck critter; if (null != (critter = animal as Duck)) { critter.Fly(); // ... } } } }
предполагая
public class Animal { } public class Dog : Animal { private string _name; public string Name { get { return _name; } set { _name = value; Console.WriteLine("Name is now " + _name); } } } public class Duck : Animal { public void Fly() { Console.WriteLine("Flying"); } }
получает вывод:
Name is now Scopey Flying
Шаблон присвоения переменных в тесте также используется при чтении байтовых блоков из потоков, например:
int bytesRead = 0; while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) { // ... }
Однако использованный выше шаблон области видимости переменных не является особенно распространенным шаблоном кода, и если бы я увидел, что он используется повсеместно, я бы искал способ его реорганизовать.
источник
if (Dog dog = animal as Dog) { ... dog ... }
Вероятно, будет в C # 6.0. Эта функция называется «выражением объявления». Увидеть
https://roslyn.codeplex.com/discussions/565640
для подробностей.
Предлагаемый синтаксис:
if ((var i = o as int?) != null) { … i … } else if ((var s = o as string) != null) { … s … } else if ...
В более общем плане предлагаемая функция состоит в том, что объявление локальной переменной может использоваться как выражение . Этот
if
синтаксис - просто приятное следствие более общей функции.источник
Try*
(например,TryParse
). Эта функция не только превращает такие вызовы в одно выражение (как и должно быть, IMO), но также позволяет более четко определить область видимости таких переменных. Я с энтузиазмом отношусь к тому, чтобыout
параметрTry
метода был ограничен его условной областью; это затрудняет внесение определенных типов ошибок.Один из методов расширения, который я часто пишу и использую *, это
public static TResult IfNotNull<T,TResult>(this T obj, Func<T,TResult> func) { if(obj != null) { return func(obj); } return default(TResult); }
Что можно было бы использовать в этой ситуации как
string name = (animal as Dog).IfNotNull(x => x.Name);
И затем
name
имя собаки (если это собака), в противном случае - null.* Я понятия не имею, работает ли это. Это никогда не было узким местом при профилировании.
источник
Здесь идет против течения, но, возможно, вы изначально делаете это неправильно. Проверка типа объекта почти всегда - это запах кода. Разве у всех животных в вашем примере нет имени? Затем просто вызовите Animal.name, не проверяя, собака это или нет.
В качестве альтернативы можно инвертировать метод, чтобы вы вызывали метод на Animal, который делает что-то по-другому в зависимости от конкретного типа Animal. См. Также: Полиморфизм.
источник
Краткое заявление
var dog = animal as Dog if(dog != null) dog.Name ...;
источник
Вот дополнительный грязный код (хотя и не такой грязный, как у Джона :-)), зависящий от изменения базового класса. Я думаю, что он отражает намерение, но, возможно, упускает из виду суть:
class Animal { public Animal() { Name = "animal"; } public List<Animal> IfIs<T>() { if(this is T) return new List<Animal>{this}; else return new List<Animal>(); } public string Name; } class Dog : Animal { public Dog() { Name = "dog"; } public string Bark { get { return "ruff"; } } } class Program { static void Main(string[] args) { var animal = new Animal(); foreach(Dog dog in animal.IfIs<Dog>()) { Console.WriteLine(dog.Name); Console.WriteLine(dog.Bark); } Console.ReadLine(); } }
источник
Если вам нужно выполнить несколько таких «как если бы» один за другим (и использование полиморфизма не вариант), рассмотрите возможность использования конструкции SwitchOnType .
источник
Проблема (с синтаксисом) не в присваивании, поскольку оператор присваивания в C # является допустимым выражением. Скорее, это с желаемым объявлением, поскольку объявления являются операторами.
Если мне нужно написать такой код, я иногда (в зависимости от более широкого контекста) буду писать такой код:
Dog dog; if ((dog = animal as Dog) != null) { // use dog }
У приведенного выше синтаксиса (который близок к запрошенному) есть свои достоинства, поскольку:
dog
внеif
приведет к ошибке компиляции , поскольку она не присваивается значение в другом месте. (То есть не назначатьdog
где-либо еще.)if/else if/...
(Их столько,as
сколько требуется для выбора подходящей ветки; это большой случай, когда я пишу его в этой форме, когда мне нужно.)is/as
. (Но тоже сDog dog = ...
формой.)Чтобы по-настоящему изолировать себя
dog
от остального мира, можно использовать новый блок:{ Dog dog = ...; // or assign in `if` as per above } Bite(dog); // oops! can't access dog from above
Удачного кодирования.
источник
вы можете использовать что-то подобное
// Объявить переменную bool temp = false;
if (previousRows.Count > 0 || (temp= GetAnyThing())) { }
источник
Еще одно ЗЛОЕ решение с методами расширения :)
public class Tester { public static void Test() { Animal a = new Animal(); //nothing is printed foreach (Dog d in a.Each<Dog>()) { Console.WriteLine(d.Name); } Dog dd = new Dog(); //dog ID is printed foreach (Dog dog in dd.Each<Dog>()) { Console.WriteLine(dog.ID); } } } public class Animal { public Animal() { Console.WriteLine("Animal constructued:" + this.ID); } private string _id { get; set; } public string ID { get { return _id ?? (_id = Guid.NewGuid().ToString());} } public bool IsAlive { get; set; } } public class Dog : Animal { public Dog() : base() { } public string Name { get; set; } } public static class ObjectExtensions { public static IEnumerable<T> Each<T>(this object Source) where T : class { T t = Source as T; if (t == null) yield break; yield return t; } }
Я лично предпочитаю чистый способ:
Dog dog = animal as Dog; if (dog != null) { // do stuff }
источник
Оператор if этого не допустит, но цикл for позволит.
например
for (Dog dog = animal as Dog; dog != null; dog = null) { dog.Name; ... }
Если то, как это работает, не сразу очевидно, то вот пошаговое объяснение процесса:
итерацию.
источник
using(Dog dog = animal as Dog) { if(dog != null) { dog.Name; ... } }
источник
IDK, если это кому-то поможет, но вы всегда можете попробовать использовать TryParse для назначения своей переменной. Вот пример:
if (int.TryParse(Add(Value1, Value2).ToString(), out total)) { Console.WriteLine("I was able to parse your value to: " + total); } else { Console.WriteLine("Couldn't Parse Value"); } Console.ReadLine(); } static int Add(int value1, int value2) { return value1 + value2; }
Общая переменная будет объявлена перед вашим если заявление.
источник
Я только что вставил оператор if, чтобы создать строку кода, которая выглядит как то, что вам интересно. Это просто помогает немного сжать код, и я обнаружил, что он более читабелен, особенно при вложении назначений:
var dog = animal as Dog; if (dog != null) { Console.WriteLine("Parent Dog Name = " + dog.name); var purebred = dog.Puppy as Purebred; if (purebred != null) { Console.WriteLine("Purebred Puppy Name = " + purebred.Name); } var mutt = dog.Puppy as Mongrel; if (mutt != null) { Console.WriteLine("Mongrel Puppy Name = " + mutt.Name); } }
источник
Я знаю, что я супер-пупер опоздал на вечеринку, но я подумал, что опубликую свой собственный обходной путь для этой дилеммы, так как я еще не видел его здесь (или где-либо еще в этом отношении).
/// <summary> /// IAble exists solely to give ALL other Interfaces that inherit IAble the TryAs() extension method /// </summary> public interface IAble { } public static class IAbleExtension { /// <summary> /// Attempt to cast as T returning true and out-ing the cast if successful, otherwise returning false and out-ing null /// </summary> /// <typeparam name="T"></typeparam> /// <param name="able"></param> /// <param name="result"></param> /// <returns></returns> public static bool TryAs<T>(this IAble able, out T result) where T : class { if (able is T) { result = able as T; return true; } else { result = null; return false; } } /// <summary> /// Attempt to cast as T returning true and out-ing the cast if successful, otherwise returning false and out-ing null /// </summary> /// <typeparam name="T"></typeparam> /// <param name="obj"></param> /// <param name="result"></param> /// <returns></returns> public static bool TryAs<T>(this UnityEngine.Object obj, out T result) where T : class { if (obj is T) { result = obj as T; return true; } else { result = null; return false; } } }
С его помощью вы можете делать такие вещи, как:
if (animal.TryAs(out Dog dog)) { //Do Dog stuff here because animal is a Dog } else { //Cast failed! animal is not a dog }
ВАЖНОЕ ПРИМЕЧАНИЕ. Если вы хотите использовать TryAs () с помощью интерфейса, вы ДОЛЖНЫ иметь этот интерфейс, наследующий IAble.
Наслаждайтесь! 🙂
источник