c# - poo - sobrecarga de operadores en c++ ejemplos sencillos
Sobrecarga del operador con programaciĆ³n basada en interfaz en C# (2)
Nos encontramos con el mismo problema y encontramos una solución excelente: patrones personalizados Resharper.
Configuramos TODOS nuestros usuarios para usar un catálogo de patrones global común además del suyo, y lo colocamos en SVN para que pueda ser versionado y actualizado para todos.
El catálogo incluye todos los patrones conocidos como incorrectos en nuestro sistema:
$i1$ == $i2$
(donde i1 e i2 son expresiones de nuestro tipo de interfaz, o derivadas.
el patrón de reemplazo es
$i1$.Equals($i2$)
y la gravedad es "Mostrar como error".
Del mismo modo, tenemos $i1$ != $i2$
Espero que esto ayude. Los catálogos de PS Global es la característica en Resharper 6.1 (EAP), se marcará como final muy pronto.
Actualización : Archivé un problema de Resharper para marcar toda la interfaz ''=='' una advertencia a menos que esté comparando con nulo. Por favor vote si cree que es una característica valiosa.
Actualización2 : Resharper también tiene el atributo [CannotApplyEqualityOperator] que puede ayudar.
Fondo
Estoy usando programación basada en interfaz en un proyecto actual y me he encontrado con un problema al sobrecargar operadores (específicamente los operadores de Igualdad y Desigualdad).
Suposiciones
- Estoy usando C # 3.0, .NET 3.5 y Visual Studio 2008
ACTUALIZACIÓN - ¡La siguiente suposición fue falsa!
- Exigir todas las comparaciones para utilizar Equals en lugar de operator == no es una solución viable, especialmente cuando pasa sus tipos a bibliotecas (como Colecciones).
La razón por la que estaba preocupado por requerir que Equals se use en lugar de operator == es que no pude encontrar en ninguna parte de las directrices de .NET que indicara que utilizaría Equals en lugar de operator == o incluso lo sugiriera. Sin embargo, después de volver a leer las Pautas para Igualar Iguales y el Operador == He encontrado esto:
Por defecto, el operador == prueba la igualdad de referencia al determinar si dos referencias indican el mismo objeto. Por lo tanto, los tipos de referencia no tienen que implementar operator == para obtener esta funcionalidad. Cuando un tipo es inmutable, es decir, los datos contenidos en la instancia no pueden modificarse, la sobrecarga del operador == para comparar la igualdad de valores en lugar de la igualdad de referencia puede ser útil porque, como objetos inmutables, pueden considerarse iguales que largos ya que tienen el mismo valor. No es una buena idea anular operador == en tipos no inmutables.
y esta interfaz equitable
La interfaz IEquatable es utilizada por objetos de colección genéricos como Dictionary, List y LinkedList cuando se prueba la igualdad en métodos tales como Contiene, IndexOf, LastIndexOf y Remove. Debe implementarse para cualquier objeto que pueda almacenarse en una colección genérica.
Contraints
- Cualquier solución no debe requerir el envío de objetos desde sus interfaces a sus tipos concretos.
Problema
- Cuando alguna vez ambos lados del operador == sean una interfaz, ninguna firma de método de sobrecarga del operador == de los tipos de concreto subyacentes coincidirá y, por lo tanto, se llamará al método predeterminado Object operator ==.
- Al sobrecargar a un operador en una clase, al menos uno de los parámetros del operador binario debe ser del tipo contenedor, de lo contrario se genera un error del compilador (Error BC33021 http://msdn.microsoft.com/en-us/library/watt39ff.aspx )
- No es posible especificar la implementación en una interfaz
Ver Código y Salida a continuación para demostrar el problema.
Pregunta
¿Cómo se proporcionan las sobrecargas de operador adecuadas para sus clases cuando se usa programación basada en interfaz?
Referencias
== Operador (Referencia de C #)
Para los tipos de valores predefinidos, el operador de igualdad (==) devuelve verdadero si los valores de sus operandos son iguales, de lo contrario es falso. Para tipos de referencia que no sean cadenas, == devuelve verdadero si sus dos operandos se refieren al mismo objeto. Para el tipo de cadena, == compara los valores de las cadenas.
Ver también
Código
using System;
namespace OperatorOverloadsWithInterfaces
{
public interface IAddress : IEquatable<IAddress>
{
string StreetName { get; set; }
string City { get; set; }
string State { get; set; }
}
public class Address : IAddress
{
private string _streetName;
private string _city;
private string _state;
public Address(string city, string state, string streetName)
{
City = city;
State = state;
StreetName = streetName;
}
#region IAddress Members
public virtual string StreetName
{
get { return _streetName; }
set { _streetName = value; }
}
public virtual string City
{
get { return _city; }
set { _city = value; }
}
public virtual string State
{
get { return _state; }
set { _state = value; }
}
public static bool operator ==(Address lhs, Address rhs)
{
Console.WriteLine("Address operator== overload called.");
// If both sides of the argument are the same instance or null, they are equal
if (Object.ReferenceEquals(lhs, rhs))
{
return true;
}
return lhs.Equals(rhs);
}
public static bool operator !=(Address lhs, Address rhs)
{
return !(lhs == rhs);
}
public override bool Equals(object obj)
{
// Use ''as'' rather than a cast to get a null rather an exception
// if the object isn''t convertible
Address address = obj as Address;
return this.Equals(address);
}
public override int GetHashCode()
{
string composite = StreetName + City + State;
return composite.GetHashCode();
}
#endregion
#region IEquatable<IAddress> Members
public virtual bool Equals(IAddress other)
{
// Per MSDN documentation, x.Equals(null) should return false
if ((object)other == null)
{
return false;
}
return ((this.City == other.City)
&& (this.State == other.State)
&& (this.StreetName == other.StreetName));
}
#endregion
}
public class Program
{
static void Main(string[] args)
{
IAddress address1 = new Address("seattle", "washington", "Awesome St");
IAddress address2 = new Address("seattle", "washington", "Awesome St");
functionThatComparesAddresses(address1, address2);
Console.Read();
}
public static void functionThatComparesAddresses(IAddress address1, IAddress address2)
{
if (address1 == address2)
{
Console.WriteLine("Equal with the interfaces.");
}
if ((Address)address1 == address2)
{
Console.WriteLine("Equal with Left-hand side cast.");
}
if (address1 == (Address)address2)
{
Console.WriteLine("Equal with Right-hand side cast.");
}
if ((Address)address1 == (Address)address2)
{
Console.WriteLine("Equal with both sides cast.");
}
}
}
}
Salida
Address operator== overload called
Equal with both sides cast.
Respuesta corta: creo que su segunda suposición puede ser defectuosa. Equals()
es la forma correcta de verificar la igualdad semántica de dos objetos, no del operator ==
.
Respuesta larga: la resolución de sobrecarga para los operadores se realiza en tiempo de compilación, no de tiempo de ejecución .
A menos que el compilador pueda conocer definitivamente los tipos de objetos a los que aplica un operador, no compilará. Como el compilador no puede estar seguro de que una dirección IAddress
va a ser algo que tiene una anulación para ==
definido, vuelve a la implementación del operator ==
predeterminado operator ==
de System.Object
.
Para ver esto más claramente, intente definir un operator +
para Address
y agregar dos instancias de IAddress
. A menos que lo envíes explícitamente a la Address
, no se compilará. ¿Por qué? Debido a que el compilador no puede decir que una IAddress
particular es una Address
, y que no existe una implementación operator +
predeterminado a la que recurrir en System.Object
.
Parte de su frustración probablemente proviene del hecho de que Object
implementa un operator ==
, y todo es un Object
, por lo que el compilador puede resolver operaciones como a == b
para todos los tipos. Cuando anula ==
, esperaba ver el mismo comportamiento pero no lo hizo, y eso se debe a que la mejor coincidencia que el compilador puede encontrar es la implementación original del Object
.
Exigir todas las comparaciones para utilizar Equals en lugar de operator == no es una solución viable, especialmente cuando pasa sus tipos a bibliotecas (como Colecciones).
En mi opinión, esto es precisamente lo que deberías estar haciendo. Equals()
es la forma correcta de verificar la igualdad semántica de dos objetos. A veces, la igualdad semántica es solo igualdad de referencia, en cuyo caso no será necesario cambiar nada. En otros casos, como en su ejemplo, anulará Equals
cuando necesite un contrato de igualdad más fuerte que la igualdad de referencia. Por ejemplo, puede considerar dos Persons
iguales si tienen el mismo número de Seguridad Social, o dos Vehicles
iguales si tienen el mismo número de identificación personal (VIN).
Pero Equals()
y operator ==
no son lo mismo. Siempre que necesite anular operator ==
, debe anular Equals()
, pero casi nunca al revés. operator ==
es más una conveniencia sintáctica. Algunos lenguajes CLR (por ejemplo, Visual Basic.NET) ni siquiera le permiten anular el operador de igualdad.