web-dev-qa-db-ja.com

ifステートメントでの割り当て

クラスAnimalとそのサブクラスDogがあります。次の行をコーディングしていることがよくあります。

if (animal is Dog)
{
    Dog dog = animal as Dog;    
    dog.Name;    
    ... 
}

変数Animal animal;の場合。

次のような記述ができる構文があります:

if (Dog dog = animal as Dog)
{    
    dog.Name;    
    ... 
}
133
michael

以下の回答は何年も前に書かれたもので、時間とともに更新されます。 C#7では、パターンマッチングを使用できます。

if (animal is Dog dog)
{
    // Use dog here
}

dogは、ifステートメントの後もスコープ内にありますが、割り当てられていないことに注意してください。


いいえ、ありません。ただし、これを書く方がより慣用的です。

Dog dog = animal as Dog;
if (dog != null)
{
    // Use dog
}

「as followed by if」がほぼalwaysのように使用されることを考えると、両方の部分を1つに実行する演算子があるほうが理にかなっているかもしれません行く。これは現在C#6ではありませんが、 パターンマッチングの提案 が実装されている場合、C#7の一部になる可能性があります。

問題は、ifステートメントの条件部分の変数をdeclareできないことです。1。私が考えることができる最も近いアプローチはこれです:

// EVIL EVIL EVIL. DO NOT USE.
for (Dog dog = animal as Dog; dog != null; dog = null)
{
    ...
}

それはただnasty...(試してみただけで機能します。しかし、どうかこれをしないでください。ああ、そしてもちろん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
});

または、次の2つを組み合わせることができます。

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ステートメントで値をassignできますが、めったにそうしません。ただし、変数を宣言することとは異なります。データのストリームを読み込むときにwhileで行うのは私にとって珍しいことではありません。例えば:

string line;
while ((line = reader.ReadLine()) != null)
{
    ...
}

最近では、foreach (string line in ...)を使用できるようにするラッパーを使用するのが普通ですが、上記をかなり慣用的なパターンと見なしています。通常、条件内に副作用があるのはではありませんが、代替手段は通常コードの重複を伴​​うため、このパターンを知っていると簡単に正しくなります。

299
Jon Skeet

asが失敗すると、nullを返します。

Dog dog = animal as Dog;

if (dog != null)
{
    // do stuff
}
48
Platinum Azure

can変数が既に存在する限り、変数に値を割り当てます。また、変数のスコープを設定して、後で同じメソッドで問題が発生した場合にその変数名を再度使用できるようにすることもできます。

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) 
{
    // ...
}

ただし、上記で使用した変数スコープのパターンは特に一般的なコードパターンではないため、場所全体で使用されているのを見た場合は、リファクタリングする方法を探しています。

12
Handcraftsman

次のような記述ができる構文があります:

if (Dog dog = animal as Dog) { ... dog ... }

C#6.0にある可能性があります。この機能は「宣言式」と呼ばれます。見る

https://roslyn.codeplex.com/discussions/56564

詳細については。

提案された構文は次のとおりです。

if ((var i = o as int?) != null) { … i … }
else if ((var s = o as string) != null) { … s … }
else if ...

より一般的には、提案された機能は、ローカル変数宣言を式として使用できることです。このif構文は、より一般的な機能の素晴らしい結果です。

11
Eric Lippert

私が頻繁に記述して使用している拡張メソッドの1つは

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です。

*これが高性能かどうかはわかりません。プロファイリングのボトルネックになることはありません。

9
Greg

ここで穀物に反するが、最初は間違っているかもしれない。オブジェクトのタイプの確認は、ほとんどの場合コードのにおいです。あなたの例では、すべての動物に名前はありませんか?次に、Animal.nameを呼び出すだけで、犬であるかどうかを確認しません。

または、メソッドを反転して、Animalの具体的なタイプに応じて異なる処理を行うAnimalのメソッドを呼び出します。ポリモーフィズムも参照してください。

5
fwielstra

より短い声明

var dog = animal as Dog
if(dog != null) dog.Name ...;
4
jmogera

複数のas-ifを1つずつ実行する必要がある場合(およびポリモーフィズムの使用はオプションではありません)、 SwitchOnType 構文の使用を検討してください。

3
Omer Raviv

C#の代入演算子は有効な式なので、問題は(構文の場合)notです。むしろ、宣言はステートメントであるため、目的の宣言を使用です。

そのようなコードを書かなければならない場合、時々(より大きなコンテキストに応じて)このようなコードを書きます。

Dog dog;
if ((dog = animal as Dog) != null) {
    // use dog
}

上記の構文には(要求された構文に近い)メリットがあります:

  1. dogoutsideを使用すると、ifは他の場所に値が割り当てられないため、コンパイルエラーになります。 (つまり、do n't assign dog他の場所。)
  2. このアプローチはif/else if/...(適切なブランチを選択するのに必要なasだけがあります; この大きなケースこのフォームで書く場所私がしなければならないとき。)
  3. is/asの重複を避けます。 (ただし、Dog dog = ...フォームでも行います。)
  4. 「イディオマティックwhile」と変わりません。 (夢中にならないでください:条件を一貫した形式でシンプルに保ちます。)

dogを他の世界から本当に隔離するには、新しいブロックを使用できます。

{
  Dog dog = ...; // or assign in `if` as per above
}
Bite(dog); // oops! can't access dog from above

ハッピーコーディング。

3
user166390

基本クラスの変更に依存する、追加のダーティコード(Jonほどダーティではありませんが、-)を次に示します。私はそれが意図を捉えていると思いますが、おそらくポイントを逃しています:

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();
    }
}
3
James Ashley

これが誰にも役立つ場合は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;
    }

total変数は、ifステートメントの前に宣言されます。

0

拡張メソッドを使用した別のEVILソリューション:)

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
}
0
Stefan Michev
using(Dog dog = animal as Dog)
{
    if(dog != null)
    {
        dog.Name;    
        ... 

    }

}
0

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);
    }
 }
0
user1689175

Ifステートメントでは許可されませんが、forループでは許可されます。

例えば.

for (Dog dog = animal as Dog; dog != null; dog = null)
{
    dog.Name;    
    ... 
}

仕組みがすぐにわからない場合は、プロセスのステップごとの説明があります。

  • 変数dogはタイプdogとして作成され、Dogにキャストされる変数animalが割り当てられます。
  • 割り当てが失敗した場合、dogはnullになり、forループの内容がすぐに中断されるため、実行されなくなります。
  • 割り当てが成功すると、forループが実行されます
    イテレーション。
  • 反復の終了時に、dog変数にnullの値が割り当てられ、forループから抜け出します。
0

あなたはそのようなものを使うことができます

//変数bool temp = falseを宣言します;

 if (previousRows.Count > 0 || (temp= GetAnyThing()))
                                    {
                                    }
0
NobDev