2018年1月13日土曜日

C# 実装の Google.ProtBuf の Timestamp 型は参照型

C# 実装の Google.ProtBuf の Timestamp 型は参照型です。 .NET の DateTime や TimeSpan などと同じで「日付だから値型だ」と思い込んでいると、わかりにくいバグの原因になります。特に == 演算子と Equals メソッドの結果が異なるのは注意が必要です。データ通信のための型と割り切って使用したほうがよいのかもしれません。

インスタンスの代入


あるインスタンスを別の変数に代入すると参照のコピーが渡されます。参照される実体は同じです。一方のプロパティの値を変更すれば、他方のプロパティの値にも影響があります。
using System.Diagnostics;
using Google.Protobuf.WellKnownTypes;

Timestamp t1 = Timestamp.FromDateTime(DateTime.Now.ToUniversalTime());
Timestamp t2 = t1;

Debug.WriteLine(t1);

t2.Nanos = 0;

Debug.WriteLine(t1);

出力結果
"2018-01-13T04:23:42.940163400Z"
"2018-01-13T04:23:42Z"
 

 

Timestamp 型をプロパティに持つ型のクローン


Person 型も protoc によって定義された型です。Birthday プロパティの型は Timestamp です。 Clone メソッドや同型のインスタンスを受け取るコンストラクタでは、「深いコピー」が行われるように実装されています。
using System.Diagnostics;
using Google.Protobuf.WellKnownTypes;

Person p1 = new Person();
p1.Birthday = Timestamp.FromDateTime(new DateTime(2018, 1, 13).ToUniversalTime());

Person p2 = new Person(p1);

Debug.WriteLine(
    string.Format("object.ReferenceEquals(p1.Birthday, p2.Birthday) --> {0}"
    , object.ReferenceEquals(p1.Birthday, p2.Birthday))
);

Debug.WriteLine(string.Format("p1.Birthday = {0}", p1.Birthday));
Debug.WriteLine(string.Format("p2.Birthday = {0}", p2.Birthday));

p1.Birthday.Seconds -= 1 * 60 * 60 * 24;

Debug.WriteLine(string.Format("p1.Birthday = {0}", p1.Birthday));
Debug.WriteLine(string.Format("p2.Birthday = {0}", p2.Birthday));


出力結果
object.ReferenceEquals(p1.Birthday, p2.Birthday) --&gt false
p1.Birthday = "2018-01-12T15:00:00Z"
p2.Birthday = "2018-01-12T15:00:00Z"
p1.Birthday = "2018-01-11T15:00:00Z"
p2.Birthday = "2018-01-12T15:00:00Z"


等値比較


Equals メソッドはオーバーライドされており、参照する実体が異なっていてもすべてのプロパティの値が一致する場合は等しいとみなされます。 ただ、== 演算子がオーバロードされていないため、Equals メソッドの結果と == による比較の結果は異なります。
using System.Diagnostics;
using Google.Protobuf.WellKnownTypes;

Timestamp t1 = Timestamp.FromDateTime(new DateTime(2018, 1, 13).ToUniversalTime());
Timestamp t2 = Timestamp.FromDateTime(new DateTime(2018, 1, 13).ToUniversalTime());

Debug.WriteLine(string.Format("t1.Equals(t2) --> {0}", t1.Equals(t2)));
Debug.WriteLine(string.Format("object.Equals(t1, t2) --> {0}", object.Equals(t1, t2)));
Debug.WriteLine(string.Format("t1 == t2 --> {0}", (t1 == t2)));

出力結果
t1.Equals(t2) --> true
object.Equals(t1, t2) --> true
t1 == t2 --> false
 

 


0 件のコメント:

コメントを投稿

paiza のスキルチェックをやってみました

いまさら感はありますが、 paiza のスキルチェックをやってみました。指定された時間内にコードを書いてユニットテストにかけ、その結果を基に評価を数値化してくれるというものですが、ゲーム感覚で空き時間を見つけて進めていこうと考えています。 どうやら時間が短いほど高い評価を得...