From: Jens Geyer Date: Mon, 29 Apr 2013 20:10:10 +0000 (+0200) Subject: THRIFT-1742 Optionally implement hashcode and equals in c# X-Git-Tag: 0.9.1~136 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=b706b1a905ce5eb8b195300c585caf53a8272c11;p=common%2Fthrift.git THRIFT-1742 Optionally implement hashcode and equals in c# Patch: Carl Yeksigian --- diff --git a/compiler/cpp/src/generate/t_csharp_generator.cc b/compiler/cpp/src/generate/t_csharp_generator.cc index b9e0b756..f0972cf1 100644 --- a/compiler/cpp/src/generate/t_csharp_generator.cc +++ b/compiler/cpp/src/generate/t_csharp_generator.cc @@ -66,6 +66,9 @@ class t_csharp_generator : public t_oop_generator iter = parsed_options.find("nullable"); nullable_ = (iter != parsed_options.end()); + iter = parsed_options.find("hashcode"); + hashcode_ = (iter != parsed_options.end()); + iter = parsed_options.find("union"); union_ = (iter != parsed_options.end()); @@ -111,6 +114,8 @@ class t_csharp_generator : public t_oop_generator void generate_csharp_struct_result_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_writer(std::ofstream& out, t_struct* tstruct); void generate_csharp_struct_tostring(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_equals(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_hashcode(std::ofstream& out, t_struct* tstruct); void generate_csharp_union_reader(std::ofstream& out, t_struct* tunion); void generate_function_helpers(t_function* tfunction); @@ -183,6 +188,7 @@ class t_csharp_generator : public t_oop_generator bool async_ctp_; bool nullable_; bool union_; + bool hashcode_; bool serialize_; bool wcf_; std::string wcf_namespace_; @@ -576,6 +582,7 @@ void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_stru // We always want a default, no argument constructor for Reading indent(out) << "public " << tstruct->get_name() << "() {" << endl; indent_up(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = (*m_iter)->get_type(); while (t->is_typedef()) { @@ -626,6 +633,10 @@ void t_csharp_generator::generate_csharp_struct_definition(ofstream &out, t_stru } else { generate_csharp_struct_writer(out, tstruct); } + if (hashcode_) { + generate_csharp_struct_equals(out, tstruct); + generate_csharp_struct_hashcode(out, tstruct); + } generate_csharp_struct_tostring(out, tstruct); scope_down(out); out << endl; @@ -1034,6 +1045,94 @@ void t_csharp_generator::generate_csharp_union_class(std::ofstream& out, t_struc indent(out) << "}" << endl << endl; } + +void t_csharp_generator::generate_csharp_struct_equals(ofstream& out, t_struct* tstruct) { + indent(out) << "public override bool Equals(object that) {" << endl; + indent_up(); + + indent(out) << "var other = that as " << type_name(tstruct) << ";" << endl; + indent(out) << "if (other == null) return false;" << endl; + indent(out) << "if (ReferenceEquals(this, other)) return true;" << endl; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + bool first = true; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + indent(out) << "return "; + indent_up(); + } else { + out << endl; + indent(out) << "&& "; + } + if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { + out << "((__isset." << (*f_iter)->get_name() << " == other.__isset." << (*f_iter)->get_name() << ") && ((!__isset." << (*f_iter)->get_name() << ") || ("; + } + t_type* ttype = (*f_iter)->get_type(); + if (ttype->is_container()) { + out << "TCollections.Equals(" << prop_name((*f_iter)) << ", other." << prop_name((*f_iter)) << ")"; + } else { + out << prop_name((*f_iter)) << ".Equals(other." << prop_name((*f_iter)) << ")"; + } + if (!field_is_required((*f_iter)) && !(nullable_ && !field_has_default((*f_iter)))) { + out << ")))"; + } + } + if (first) { + indent(out) << "return true;" << endl; + } else { + out << ";" << endl; + indent_down(); + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +void t_csharp_generator::generate_csharp_struct_hashcode(ofstream& out, t_struct* tstruct) { + indent(out) << "public override int GetHashCode() {" << endl; + indent_up(); + + indent(out) << "int hashcode = 0;" << endl; + indent(out) << "unchecked {" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_type* ttype = (*f_iter)->get_type(); + indent(out) << "hashcode = (hashcode * 397) ^ "; + if (field_is_required((*f_iter))) { + out << "("; + } else if ( nullable_) { + out << "(" << prop_name((*f_iter)) << " == null ? 0 : "; + }else { + out << "(!__isset." << (*f_iter)->get_name() << " ? 0 : "; + } + if (ttype->is_container()) { + out << "(TCollections.GetHashCode(" + << prop_name((*f_iter)) + << "))"; + } else { + out << "(" + << prop_name((*f_iter)) + << ".GetHashCode())"; + } + out << ");" << endl; + } + + indent_down(); + indent(out) << "}" << endl; + indent(out) << "return hashcode;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + void t_csharp_generator::generate_service(t_service* tservice) { string f_service_name = namespace_dir_ + "/" + service_name_ + ".cs"; f_service_.open(f_service_name.c_str()); @@ -2394,6 +2493,7 @@ THRIFT_REGISTER_GENERATOR(csharp, "C#", " wcf: Adds bindings for WCF to generated classes.\n" " serial: Add serialization support to generated classes.\n" " nullable: Use nullable types for properties.\n" +" hashcode: Generate a hashcode and equals implementation for classes.\n" " union: Use new union typing, which includes a static read function for union types.\n" ) diff --git a/lib/csharp/Makefile.am b/lib/csharp/Makefile.am index 9b07f4fb..f13f90a0 100644 --- a/lib/csharp/Makefile.am +++ b/lib/csharp/Makefile.am @@ -19,6 +19,7 @@ THRIFTCODE= \ src/Collections/THashSet.cs \ + src/Collections/TCollections.cs \ src/Properties/AssemblyInfo.cs \ src/Protocol/TAbstractBase.cs \ src/Protocol/TBase.cs \ diff --git a/lib/csharp/src/Collections/TCollections.cs b/lib/csharp/src/Collections/TCollections.cs new file mode 100644 index 00000000..23fd8da3 --- /dev/null +++ b/lib/csharp/src/Collections/TCollections.cs @@ -0,0 +1,94 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +using System; +using System.Collections; + +namespace Thrift.Collections +{ + public class TCollections + { + /// + /// This will return true if the two collections are value-wise the same. + /// If the collection contains a collection, the collections will be compared using this method. + /// + public static bool Equals (IEnumerable first, IEnumerable second) + { + if (first == null && second == null) + { + return true; + } + if (first == null || second == null) + { + return false; + } + IEnumerator fiter = first.GetEnumerator (); + IEnumerator siter = second.GetEnumerator (); + + bool fnext = fiter.MoveNext (); + bool snext = siter.MoveNext (); + while (fnext && snext) + { + IEnumerable fenum = fiter.Current as IEnumerable; + IEnumerable senum = siter.Current as IEnumerable; + if (fenum != null && senum != null) + { + if (!Equals(fenum, senum)) + { + return false; + } + } + else if (fenum == null ^ senum == null) + { + return false; + } + else if (!Equals(fiter.Current, siter.Current)) + { + return false; + } + fnext = fiter.MoveNext(); + snext = siter.MoveNext(); + } + + return fnext == snext; + } + + /// + /// This returns a hashcode based on the value of the enumerable. + /// + public static int GetHashCode (IEnumerable enumerable) + { + if (enumerable == null) + { + return 0; + } + + int hashcode = 0; + foreach (Object obj in enumerable) + { + IEnumerable enum2 = obj as IEnumerable; + int objHash = enum2 == null ? obj.GetHashCode () : GetHashCode (enum2); + unchecked + { + hashcode = (hashcode * 397) ^ (objHash); + } + } + return hashcode; + } + } +} \ No newline at end of file diff --git a/lib/csharp/src/Thrift.WP7.csproj b/lib/csharp/src/Thrift.WP7.csproj index be38bc83..3d5b027b 100644 --- a/lib/csharp/src/Thrift.WP7.csproj +++ b/lib/csharp/src/Thrift.WP7.csproj @@ -70,6 +70,7 @@ + diff --git a/lib/csharp/src/Thrift.csproj b/lib/csharp/src/Thrift.csproj index 0263d728..0722c18b 100644 --- a/lib/csharp/src/Thrift.csproj +++ b/lib/csharp/src/Thrift.csproj @@ -1,4 +1,4 @@ - +