ごまなつ Blog

楽しく働ける世界を目指して

【C#】クラスの配列要素の代入は、シャローコピーになる

C#で、xmlファイルをシリアライズ・デシリアライズするためのクラスを宣言して、その内容を変更していました。そこで、配列要素の代入なのにシャローコピーに苦しめられたのでメモです。

public class exampleclass
    {
        [System.Xml.Serialization.XmlElement("version")]
        public string version { get; set; }
        [System.Xml.Serialization.XmlElement("id")]
        public string mapid { get; set; }
        [System.Xml.Serialization.XmlElement("name")  
        public string mapname { get; set; }
        [System.Xml.Serialization.XmlElement("element")]
        public List<band> element { get; set; }
    }

public exampleclass[] Array1 = new Chmap[1000

要素名は適当ですが、このようなクラスがあり、そのクラスの2つの配列を宣言します。この後、

Array1[4] = Array1[0]
Array1[4].version = "1.99"

のように要素を別のインデックスにコピーした後、その要素を変更します。すると、Array1[0].versionも"1.99"に変更されてしまいます。

ディープコピーとシャローコピー

ディープコピー(深いコピー)とシャローコピー(浅いコピー)の違いは、簡単に言うとコピー先の変更がコピー元にも反映されるのがシャローコピー、そうでないのがディープコピーです。シャローコピーは参照をコピーしているだけなので、同じものを参照しているのでどちらも変更された後の配列を参照していることになります。

C#では、配列やリストを=で代入すると、シャローコピーになります。配列やリストは参照型です。int,doubleといった値は値型なのでディープコピーになります。

では配列やリストをどうやってディープコピーするのかですが、配列はArray.Copy,リストはコンストラクタで宣言します。

int[] a = new int[5]
int[] b = new int[5]
Array.Copy(a, b, a.Length)

var list  = new List<int>{1,2}
var list2 = new List<int>(list)

これで良いかと思われたのですが、今回のクラスの場合のような多重構造になると、直下はディープコピーになりますが、それ以降の階層ではシャローコピーになります。

参考にしたページ qiita.com

自分が採用した方法

こちらのページのメソッドを使用しました。

l-s-d.sakura.ne.jp

多重構造でもディープコピーしたい場合は自力で実装するしかないようです。MemoryStreamを用いてシリアライズ・デシリアライズしているようです。

【C#】ウィンドウの大きさに応じてパーツの大きさも変えたい

デフォルトでは、ウィンドウを最大化しても配置されたパーツはその大きさのままで、変更されません。そのため、不自然になります。ウィンドウの大きさに追従して、パーツを大きくするためには、Anchorプロパティを変更します。

Anchorプロパティ

ウィンドウが大きくなった時に、ウィンドウの端との距離を固定するかどうかを設定するプロパティです。要するに、追従させるってことですね。Top, Bottom, Left, Rightの4つを複数個から指定できます。

プロパティウィンドウで指定するときは、白ではなく灰色になっている方向が指定されています。

すべて指定していれば、大きさに追従するようになります。

パーツの上に載っているパーツは、どちらのパーツも指定しないと追従しない

例えば、Tabcontrolで3つのページを作り、それぞれにListViewを載せたとします。この時、ListViewだけにAnchorを指定しても追従しません。TabcontrolのAnchorも指定しましょう。

【C#】配列のコピーは=でやってはいけない

基本的な話です。

配列aを配列bにコピーして、その後配列bの要素を変更したいとします。

a[3]=[1,2,3]
b[3];
a=b;
b[3]= b[3]+2;

普通にコピーするだけならこれでいいだろ、と思うのですが、これでは参照ごとコピーされ、a[3]の値も変更されてしまいます。つまり、a=bとした場合、aが変更されたらbが、bが変更されたらaも変更されてしまいます。よって、どちらも変更したい場合でなければ、Array.Copyを使いましょう。

Array.Copy(コピー元、コピー先、長さ)

この動作を知っているとなにかの役に立つかもしれません。

【C#】ListViewで移動禁止にしたい

結論

private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            if (e.IsSelected && (e.Item != selectedItem))
            {
                if (isChanged)  
                {
                    e.Item.Selected = false;
                    selectedItem.Selected = true;                   
                    if (Counter)
                    {
                        Counter = false;
                        MessageBox.Show("移動できません");
                    }
                    else
                    {
                        Counter = true;
                    }
                }
                else
                {
                    selectedItem = e.Item;
                }
            }
        }
        private void listView1_MouseUp(object sender, MouseEventArgs e)
        {
            selectedItem.Selected = true;   //これが無いと選択状態が消える
        }

動作

イベントハンドラで、選択されているアイテムが変更されるときに、選択されているアイテムがイベントハンドラを発生させていて、アイテムは異なるときに変更があったら選択を無効化して選択されたアイテムに戻しています。 このCounterというのは、選択を変更する一連の動作がItemSelectionChangedを2回呼ぶので、1回しかメッセージボックスを出さないように入れています。

MouseUpを入れておかないと、選択状態が消えます。listViewはほかの場所をクリックすると選択が消えますよね。

【C#】ListView_SelectIndexChangedは、一瞬-1がインデックスに入る

ListView_SelectIndexChangedは、ListViewの選択されているアイテムのインデックスが変更された時に発火するイベントハンドラです。なので、処理は

private void ListView1_SelectedIndexChanged(object sender, EventArgs e)
        {
             //何らかの処理       
        }

となりますが、これではエラーになります。なぜか。それは、選択されているアイテムのインデックスが変更されるとき、一瞬-1がインデックスに入るからです。aからbに選択されているインデックス変更するには、

  1. aが選択されている
  2. 選択を外す
  3. bを選択する

という処理順番をとっているようです。2の状況では、インデックスが-1になり、存在しないインデックスにアクセスしようとしてエラーが起きています。よって、

private void ListView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ListView1.SelectedItems.Count > 0)//
            {
                //何らかの処理
            }      
        }

とすることで、存在しないインデックスにアクセスしようとする状況を回避することでエラーを回避できます。

「アジャイルな強いチームを作るチームビルディング超実践ガイド」を読んでの感想

TL;DR

チームビルディングのやり方がわからない人にぜひ読んでほしい。意義とやり方がわかる。 同人誌なのにカバーと帯がついてるよ。まるで商業誌のようになってるよ

チームビルディング

私は組み込み系の職場であり、一人が担当製品のソフト面、一人が担当製品のハード面を担当するといった仕組みなので、いわゆるチーム開発というものがよくわかっていません。ですが、この本を読むことでチームビルディングがなぜ必要なのかわかりました。それがわかってもやり方がわからないじゃん、となるのですがいろいろな手法が紹介されています。同じ方向をチーム全員が向けるもの、多様性を認めるもの、関係の質を求めるもの、文化を作るものがあります。チームといってもまだチームになっていない状態から、自己組織化できているチームまであるのでそれぞれのチームに必要なチームビルディングの手法が載っていると考えます。

チームが何のためにあるのか、というと良い製品を作ることだと思います。いい製品を作るための要素には、チームメンバーがそれぞれ活躍していくことがあると思います。活躍するためには、そのメンバーの力だけでなくチームの力も必要だと思います。チームの力が発揮されるためには、全員が良い製品を作るという想いを持ち、その製品のイメージが全員で共有されていることやチームメンバーの関係が良いことが必要だと考えます。エンジニアだって人間なので、人間関係の悪さが影響するのは当然のことです。このチームの力を発揮するために、チームビルディングが必要だと考えます。

自分の陸上競技の経験から

陸上競技をやっていた時に4×100mリレーをやってましたが、チームのタイムは単純に4人の100mのタイムを合計したよりも早くなります。物理的にはセパレートゾーンで加速できるからなどいろいろ要因があるんですが、心理的には全員が速くゴールする意識を持っていることもあると思います。

バトンを渡すメンバーと喧嘩をした時、バトンパスの精度が悪くなりました。相手のことを理解できていると、今日は調子がよさそうだから自分はセパレートゾーンを走る距離を短くしようとか、顔を見るとスタートしようとしているときの相手の気持ちがわかるようになったりします。不測の事態になった時の修正は、相手を理解することも必要だと感じました。

チームビルディングはエンジニアの製品開発において注目されてきましたが、スポーツではもともと重要視されていたので、根本では共通点が多くあるのかもしれないと感じました。スポーツから製品開発に学ぶこと、製品開発からスポーツに学ぶことがあるとよりよい世界になっていきそうですね。

多くのボードゲームはなぜ4人用なのか

私の持論ではありますが、なぜ個人勝負のボードゲームが4人用なのか考えたことを書きます。

TL;DR

2人用だと万人受けしない、3人用だと一人勝ちする人が出る、5人以上用だとゲームが長くなりがち。4人用だと家族でやりやすいといった点もある(ヨーロッパの場合)

ボードゲームで勝つために必要なもの

ボードゲームで勝つために必要なものは、スキルと運です。スキルは、そのゲームのうまさです。運は、その状況に最適な効果を起こせることです。たいていのボードゲームは、勝つためにはスキルと運をどちらもバランスよくできると勝てるようになっています。たまに運だけで勝てることもあります。 人間は、運だけで勝つと、「スキル関係ないやん、運ゲー。つまらん」となりますし、スキルだけで勝つと「最初からスキル差あるから勝てん。つまらん」となります。 スキルだけで勝つボードゲームは将棋、チェス、囲碁などあります。これは面白いと思っている人もいますよね。いうなれば、スキルだけで勝つゲームを好むのはゲームを積極的にプレーする、ゲーム好きの上級者です。反対に、ゲームを積極的にプレーしない人は、スキル差があるから勝てない、運で勝てるゲームがやりたいとなって、運が絡むゲームを好みます。つまり、万人に受けるためには、勝つために運とスキル両方が必要にする必要があるのです。

2人用のゲーム

2人用のゲームは、スキル差が如実に表れがちです。また、運を絡めようとすると今度は運ゲーになりがちです。このバランスをとるのがとても難しく、スキルを求める上級者のみに受けるか、運を求める人のみに受けるかとなりがちなのです。2人用のゲームは心理戦が多いですね。これだと、スキル以外のものを求めるので面白いですよね。

3人用のゲーム

ボードゲームは勝つために他のプレイヤーの妨害をできるようになっている場合があります。このようなゲームの場合、A、B、Cの3人のプレイヤーがいたとします。Aがかなり調子よく勝っていたとします。このときBが妨害に入った時、Cは妨害が入らなくなりますよね。妨害に入るとその分自分の行動を制限することになるので、妨害に入ったほうが不利になります。しかし、妨害しないとAが勝ちますが妨害するとCが勝ちます。この状態がCにも当てはまります。また、BとCが協力してAの妨害に入った場合、Aは完全に勝てなくなります。よって、最初に勝っても最終的に勝てないし、妨害に入っても勝てないというゲームになりがちです。

4人用のゲーム

先ほど3人用のゲームで述べたとおり、ほかのプレイヤーを妨害できるゲームではA、B、C、DがいたとしてAがBの妨害に入りCがDの妨害に入るといった、妨害がうまく働きます。協力して妨害に入ろうとしても、他のプレイヤーが3人いるのでそれぞれのプレイヤーで立場が異なり妨害に入るか自分が利益を得るかで状況が移り変わっていくというのも面白いです。

あと、ボードゲームはドイツなどのヨーロッパ発祥です。ヨーロッパは白夜があったりかなり寒かったりと外出しにくい気候の時家にいます。その時、友人や家族とボードゲームをやる文化があります。4人だと家族や友人でちょうどいい人数というのもあるそうです。

5人以上用のゲーム

単純にゲームが長くなりがちです。ルールも複雑になりやすく、どちらかというとパーティーゲーム寄りになります。パーティーゲームだと人数が多いほうが楽しいです。

ボードゲームのルールの一番最初は、人数を集めることです。5人以上集めるの大変です。