A Slice struct can be mapped to a C# struct or class, depending on the Slice directive used. For the comparison methods and equality operators, slice2cs generates the same code for classes and structs. However, this code is not optimal for either case, since it tries to satisfy both.
As an example let's use this Slice declaration:
Code:
struct A { string str; int num; };
From
http://msdn.microsoft.com/library/de...pconEquals.asp
and
http://msdn.microsoft.com/library/de...lsoperator.asp
the basic guidelines are these:
* For value types, whenever you override Equals (instance method), you
should also overload the equality operators, as value types do not have
a default implementation for them.
* For reference types, do not overload the equality operators, as there is
a default implementation which uses the Equals() method. See
http://msdn.microsoft.com/library/de...alitytopic.asp.
* You rarely ever need to re-implement the *static* Equals method, as its
default implementation uses the instance method. From
http://msdn.microsoft.com/library/de...qualsTopic.asp
it becomes clear that the implementation generated by slice2cs just
duplicates the default.
The generated code look like this, the comments are based on the guidelines above:
Code:
public override bool Equals(object __other)
{
// for struct mapping,this will likely box "this", always returning false
if(object.ReferenceEquals(this, __other))
{
return true;
}
// This will evaluate to true for any two subclasses of A;
// this may be OK if is it desired that two different instances
// are equal even if only their A portion is equal and they
// don't override Equals(). Seems unusual.
if(!(__other is A))
{
return false;
}
// If we want to avoid multiple "(A)__other" casts below, we could
// use A other = (A)__other, but for structs this may be inefficient
// as it leads to a struct copy, whereas otherwise the compiler
// may optimize away the struct instantiation.
// Why not use string.Equals(this.str, ((A)__other).str)?
if(str == null)
{
if(((A)__other).str != null)
{
return false;
}
}
else
{
if(!(str.Equals(((A)__other).str)))
{
return false;
}
}
if(!(num.Equals(((A)__other).num)))
{
return false;
}
return true;
}
// redundant - as per guidelines
public static bool Equals(A __lhs, A __rhs)
{
return object.ReferenceEquals(__lhs, null)
? object.ReferenceEquals(__rhs, null)
: __lhs.Equals(__rhs);
}
// overloading equality operators is recommended for value types,
// but not for reference types, so we should have two kinds of code
public static bool operator==(A __lhs, A __rhs)
{
return Equals(__lhs, __rhs);
}
public static bool operator!=(A __lhs, A __rhs)
{
return !Equals(__lhs, __rhs);
}
For struct mapping:
===================
Here is how it could be done specifically for C# structs:
(simpler because structs cannot be null or derived from)
Code:
public override bool Equals(object __other)
{
// we can use 'is' because there are no structs derived from A
return __other is A && this == (A)__other;
}
// no null checks necessary - we have structs as arguments
public static bool operator==(A __lhs, A __rhs)
{
return string.Equals(__lhs.str, __rhs.str) && __lhs.num.Equals(__rhs.num);
}
public static bool operator!=(A __lhs, A __rhs)
{
return !(__lhs == __rhs);
}
For class mapping:
==================
No static Equals() implementation, no operator overloads.
This would be a simpler approach, as it also avoids repeated casts.
Code:
public override bool Equals(object __other)
{
// we could use "if (!(__other is A))" depending on the
// desired semantics - the ususal (MSDN) way is shown below.
if (__other == null || GetType() != __other.GetType())
return false;
// avoid multiple casts, no value type copying here
A other = (A)__other;
// note we are not using ==, since value types do not have a default implementation for ==
return string.Equals(this.str, other.str) && num.Equals(other.num);
}