I have a generic interface, and a few classes implement it.
then from a global place, I want to use methods from that interface,
yet, I don't know their compiled generic type, so the reference is only their object's class, as showing in runtime. (so I can't get access to the interface methods)
a few questions:
Is it possible to use them?
should I design it without generics?
what is the purpose of generic interfaces if I can't use them at runtime?
can generics out/in or dynamic help in that situation?
edit: some example code
public interface IMyInterface<T>
where T: class, new()
{
void Delete (T obj);
}
public class trigger {}
public class triggervm : IMyInterface<trigger>
{
List<trigger> _trigList = new List<trigger>()
public void Delete (trigger obj)
{
_trigList.Remove (obj);
}
}
now, say I want to check, and then use the method Delete, from a "global" place:
if (chosenItem is IMyInterface<???>)
{
var item = chosenItem as IMyInterface<???>;
item.Delete(someObj);
}
Here's an example of how you can use the functions of a generic interface without having to know what the generic types are when writing the code.
The basic idea is to call your own generic function (in this case CompareValuesInternal) and use reflection to extract the appropriate type information to pass along with the call.
sm_compare_values_info = typeof(YourType).GetMethod("CompareValuesInternal", BindingFlags.NonPublic | BindingFlags.Static);
static public bool CompareValues(object x, object y)
{
bool result = true;
if ((x == null && y != null) || (x != null && y == null))
{
result = false;
}
else if (x == null && y == null)
{
result = true;
}
else if (x is IComparer)
{
result = ((x as IComparer).Compare(x, y) == 0);
}
else if (x is IComparable)
{
result = ((x as IComparable).CompareTo(y) == 0);
}
else if (x is IEqualityComparer)
{
result = (x as IEqualityComparer).Equals(x, y);
}
else if (x.GetType() != y.GetType())
{
result = false;
}
else
{
//----IMPORTANT PART----
MethodInfo info = sm_compare_values_info.MakeGenericMethod(x.GetType());
result = (bool)info.Invoke(null, new object[] { x, y });
}
return result;
}
static protected bool CompareValuesInternal<T>(T x, T y)
{
bool result = false;
if (x is IEqualityComparer<T>)
{
result = (x as IEqualityComparer<T>).Equals(x, y);
}
else if (x is IEquatable<T>)
{
result = (x as IEquatable<T>).Equals(y);
}
else if (x is IComparable<T>)
{
result = ((x as IComparable<T>).CompareTo(y) == 0);
}
else if (x is IComparer<T>)
{
result = ((x as IComparer<T>).Compare(x, y) == 0);
}
return result;
}
Yes, using dynamic can be the simplest and best way to cope with this issue. Jon Skeet's C# in action mentions this as well in the section about dynamics.
As others said, more code less english and we all can narrow this down here.
like thomas suggested, I use dynamic,
and like "RedHat" suggested, I have the enclosing class inherit from both my interface (IClipboard<T>) and a parent interface (IClippable) that is just a "grouping" interface.
myinterface inherits from the grouping interface, so any class that implements myinterface also matches the grouping one.
so I can check if the selected item is IClippable.
the cleanest I could get with my current knowledge of the language.
better support in C# would be nice.
interface IInterface<T>
{
Type GetMyType();
}
class MyClass1 : IInterface<int>
{
public Type GetMyType()
{
return typeof(int);//each subclass must be return own generic type
}
}
static void Main()
{
new MyClass1().GetMyType()==typeof(int);//Is True
}
Related
I am doing unit testing, and basically want to check that the data that 2 objects hold is the same
Assert.AreEqual(object1, object2);
Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
I am searching for the C# equivalent of assertJ
Assert.That(object1).isEqualToComparingFieldByField(object2)
You could either use records (c# 9 +) or you have to override the Equals method (if you have access and you can change the objects that you're working with).
Records example:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
with classes:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
if you are not allowed to touch the implementation, then you could use serialization and compare the strings:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
using Newtonsoft.Json nuget
C# classes are reference equality, which means that variables are the same using the standard Equals and == if they point to the same object, you could override that behaivour, but it may break something now or in the future.
Or, you could switch to using a construct that's value equality by default, which structs as well as record classes are. If you can't (or don't want to) do that you can implement a value equals "helper" method yourself. I would not recommend overriding the Equals method or the == operator, as that can (and most likely will) lead to errors in the future instead I recommend you write your own ValueEquals method or extension method, something along the lines of
class Foo
{
public int Count {get; set;}
public string Message {get; set;}
}
public static bool ValueEquals(this Foo self, Foo other)
{
return self.Count == other.Count && self.Message == other.Message;
}
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(myFoo1.ValueEquals(myFoo2));
}
Depending on whether or not you can/ want to add a ValueEquals to your Foo class you can decide on doing it with an extension method or a normal method.
You could also implement a IEqualityComparer<T> like
public class FooValueEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo foo1, Foo foo2)
{
return foo1.Count == foo2.Count &&
foo1.Message == foo2.Message;
}
public int GetHashCode(Foo foo)
{
return foo.GetHashCode();
}
}
// Use it
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(new FooEqualityComparer().Equals(myFoo1, myFoo2));
}
Or, you could write a generic ValueEquals that works for all^* classes using Reflection:
public static class ValueEqualityComparer
{
public static bool ValueEquals<T>(this T self, T other) where T : class
{
var type = self.GetType();
if (type == typeof(string))
return self.Equals(other);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var selfValue = property.GetValue(self);
var otherValue = property.GetValue(other);
// String is special, it's not primitive but is value equality like primitives
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
{
if (!selfValue.Equals(otherValue))
return false;
}
// If the property is a List value equals each member
// Maybe find another type that allows indexing and is less restrictive
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var selfList = ((IEnumerable)property.GetValue(self)).Cast<object>();
var otherList = ((IEnumerable)property.GetValue(other)).Cast<object>();
try
{
// Using EquiZip from MoreLinq: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs
foreach (var element in selfList.EquiZip(otherList, (selfItem, otherItem) => new { selfItem, otherItem }))
{
if (!ValueEquals(element.selfItem, element.otherItem))
return false;
}
}
catch (InvalidOperationException)
{
// MoreLINQ throws a InvalidOperationException if our two enumerables aren't the same length
return false;
}
}
else
{
if (!ValueEquals(selfValue, otherValue))
return false;
}
}
return true;
}
}
This implementation is by no means perfect, and should honestly only be used for UnitTests and also should be thoroughly tested itself. You can see my tests as a dotnetfiddle here
Or you could do it "dirty" and serialize the objects to a string and compare those values.
I want to sort the list of classes and interfaces. So my logic is
if the class is not implemented interface it's higher than the interface, otherwise, it's lower
I am using the IComparer interface to sort my list. My model looks like this:
My Comparer class (if returns 1 it means y>x, 0 is x==y -1 is x>y):
public class SortedTypeComparer : IComparer<Type>
{
/// <summary>
/// Compares types
/// </summary>
public int Compare(Type x, Type y)
{
public int Compare(Type x, Type y)
{
if (y.IsAssignableFrom(x))
{
return 1;
}
else if (x.IsAssignableFrom(y))
{
return -1;
}
else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsInterface && y.IsClass)
{
return 1;
}
else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsClass && y.IsInterface)
{
return -1;
}
else
{
return 0;
}
}
}
}
I am expecting when I sort the list it should be:
IAnimal
IRunnable
Animal
ICat
Cat
Or:
IRunnable
IAnimal
Animal
ICat
Cat
Because IRunnable and IAnimal are 'equal'. Here is my usage:
var list = new List<Type>();
list.Add(typeof(IAnimal));
list.Add(typeof(IRunnable));
list.Add(typeof(ICat));
list.Add(typeof(Animal));
list.Add(typeof(Cat));
list.Sort(new SortedTypeComparer());
In this case, it is working as expected. But when I change the order of adding to list for example(put IRunnable to the end):
var list = new List<Type>();
list.Add(typeof(IAnimal));
list.Add(typeof(ICat));
list.Add(typeof(Animal));
list.Add(typeof(Cat));
list.Add(typeof(IRunnable));
list.Sort(new SortedTypeComparer());
The order is
IAnimal
Animal
ICat
IRunnable
Cat
It is not my expectation because of IRunnable>Animal. It seems when it compare Animal and ICat Animal is higher, then when it compares ICat and IRunnable it's saying "ICat == IRunnable, so Animal should be > IRunnable". How can I write the logic in the Compare method to sort my list as expected?
I do not think this is possible with a IComparer. From CompareTo
For objects A, B, and C, the following must be true:
If A.CompareTo(B) returns zero and B.CompareTo(C) returns zero, then A.CompareTo(C) is required to return zero.
So if A inherits from C and B does not inherit anything, then according to your rules compareTo should return:
A.CompareTo(B) -> 0
B.CompareTo(C) -> 0
A.CompareTo(C) -> 1
This violates the requirements of CompareTo.
An alternative would be to build an Directed acyclic graph of the hierarchy. Then you should be able to use Topological sorting to sort the graph.
Maybe answer of #JonasH more correct, but I did by fixing My comparer class. I added the following condition in my method and it is correct for most scenarios:
public class SortedTypeComparer : IComparer<Type>
{
public List<Type> AllTypes { get; set; }
public SortedTypeComparer(List<Type> types)
{
AllTypes = types;
}
/// <summary>
/// Compares types
/// </summary>
public int Compare(Type x, Type y)
{
var result = CompareIsHigherOrLower(x, y);
if (result == 0)
{
var subEntitiesOfX = AllTypes.Where(a => x.IsAssignableFrom(a) && a != x);
foreach (var subTypeOfX in subEntitiesOfX)
{
result = CompareIsHigherOrLower(subTypeOfX, y);
if (result == -1)
{
return -1;//It means SubEntity of X is higher then Y and X should be > Y
}
}
var subEntitiesOfY = AllTypes.Where(a => y.IsAssignableFrom(a) && a != y);
foreach (var subType in subEntitiesOfY)
{
result = CompareIsHigherOrLower(subType, x);
if (result == -1)
{
return 1;//It means SubEntity of Y is higher then X and Y should be > X
}
}
}
return result;
}
int CompareIsHigherOrLower(Type x, Type y)
{
if (y.IsAssignableFrom(x))
{
return 1;
}
else if (x.IsAssignableFrom(y))
{
return -1;
}
else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsInterface && y.IsClass)
{
return 1;
}
else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsClass && y.IsInterface)
{
return -1;
}
else
{
return 0;
}
}
}
Using reflection, I'm attempting to find the set of types which inherit from a given base class. It didn't take long to figure out for simple types, but I'm stumped when it comes to generics.
For this piece of code, the first IsAssignableFrom returns true, but the second returns false. And yet, the final assignment compiles just fine.
class class1 { }
class class2 : class1 { }
class generic1<T> { }
class generic2<T> : generic1<T> { }
class Program
{
static void Main(string[] args)
{
Type c1 = typeof(class1);
Type c2 = typeof(class2);
Console.WriteLine("c1.IsAssignableFrom(c2): {0}", c1.IsAssignableFrom(c2));
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
generic1<class1> cc = new generic2<class1>();
}
}
So how do I determine at run time whether one generic type definition is derived from another?
From the answer to another question:
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
return true;
}
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return IsAssignableToGenericType(baseType, genericType);
}
The exact code you posted does not return surprising results.
This says "false":
Type g1 = typeof(generic1<>);
Type g2 = typeof(generic2<>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
This says "true":
Type g1 = typeof(generic1<class1>);
Type g2 = typeof(generic2<class1>);
Console.WriteLine("g1.IsAssignableFrom(g2): {0}", g1.IsAssignableFrom(g2));
The difference is that open generic types cannot have instances, so one is not "assignable" to the other.
From the docs:
Returns true if c and the current
Type represent the same type, or if
the current Type is in the
inheritance hierarchy of c, or if
the current Type is an interface
that c implements, or if c is a
generic type parameter and the current
Type represents one of the
constraints of c. false if none of
these conditions are true, or if c
is null.
In this case, clearly none of these conditions are true. And there's an extra note:
A generic type definition is not
assignable from a closed constructed
type. That is, you cannot assign the
closed constructed type
MyGenericList<int> (MyGenericList(Of Integer) in Visual Basic) to a
variable of type MyGenericList<T>.
In the following case use the method Konrad Rudolph provided could be wrong, like: IsAssignableToGenericType(typeof(A), typeof(A<>));// return false
I think here's a better answer
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
the test case, see Using IsAssignableFrom with C# generics for detail
using System;
/**
* Sam Sha - yCoder.com
*
* */
namespace Test2
{
class MainClass
{
public static void Main (string[] args)
{
string a = "ycoder";
Console.WriteLine(a is object);
A aa = new A();
//Console.WriteLine(aa is A<>);//con't write code like this
typeof(A<>).IsAssignableFrom(aa.GetType());//return false
Trace(typeof(object).IsAssignableFrom(typeof(string)));//true
Trace(typeof(A<>).IsAssignableFrom(typeof(A)));//false
AAA aaa = new AAA();
Trace("Use IsTypeOf:");
Trace(IsTypeOf(aaa, typeof(A<>)));
Trace(IsTypeOf(aaa, typeof(AA)));
Trace(IsTypeOf(aaa, typeof(AAA<>)));
Trace("Use IsAssignableFrom from stackoverflow - not right:");
Trace(IsAssignableFrom(typeof(A), typeof(A<>))); // error
Trace(IsAssignableFrom(typeof(AA), typeof(A<>)));
Trace(IsAssignableFrom(typeof(AAA), typeof(A<>)));
Trace("Use IsAssignableToGenericType:");
Trace(IsAssignableToGenericType(typeof(A), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AA), typeof(A<>)));
Trace(IsAssignableToGenericType(typeof(AAA), typeof(A<>)));
}
static void Trace(object log){
Console.WriteLine(log);
}
public static bool IsTypeOf(Object o, Type baseType)
{
if (o == null || baseType == null)
{
return false;
}
bool result = baseType.IsInstanceOfType(o);
if (result)
{
return result;
}
return IsAssignableFrom(o.GetType(), baseType);
}
public static bool IsAssignableFrom(Type extendType, Type baseType)
{
while (!baseType.IsAssignableFrom(extendType))
{
if (extendType.Equals(typeof(object)))
{
return false;
}
if (extendType.IsGenericType && !extendType.IsGenericTypeDefinition)
{
extendType = extendType.GetGenericTypeDefinition();
}
else
{
extendType = extendType.BaseType;
}
}
return true;
}
//from stackoverflow - not good enough
public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
if (it.IsGenericType)
if (it.GetGenericTypeDefinition() == genericType) return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return baseType.IsGenericType &&
baseType.GetGenericTypeDefinition() == genericType ||
IsAssignableToGenericType(baseType, genericType);
}
}
class A{}
class AA : A{}
class AAA : AA{}
}
I have a different Approach that resolves this issue, Here are my classes
public class Signal<T>{
protected string Id {get; set;} //This must be here, I use a property because MemberInfo is returned in an array via GetMember() reflection function
//Some Data and Logic And stuff that involves T
}
public class OnClick : Signal<string>{}
Now if I have an instance of type OnClick but I dont know that, and I want to find out if I have an instance of anything which inherits from Signal<> of any type? I do this
Type type = GetTypeWhomISuspectMightBeAGenericSignal();
PropertyInfo secretProperty = type.GetProperty("Id", BindingFlags.NonPublic | BindingFlags.Instance);
Type SpecificGenericType = secretProperty.DeclaringType; //This is the trick
bool IsMyTypeInheriting = SpecificGenericType.IsGenericType && SpecificGenericType.GetGenericTypeDefinition() == typeof(Signal<>); //This way we are getting the genericTypeDefinition and comparing it to any other genericTypeDefinition of the same argument length.
So this works for me, its not recursive, and it uses a trick via a designated property. It has limitations that its hard to write a function that checks assignability for all generics ever. But for a specific type it works
Obviously you need to check if() conditions better and stuff, but these are the Raw lines required to evaluate assignability of a type to its base generic, this way.
Hope this helps
My two cents. IMHO it doesn't make much sense to separate implements, derives or the original functionality of IsAssignableFrom,
Constructing from the answers previously given, this is how I do it:
public static bool ImplementsOrDerives(this Type #this, Type from)
{
if(from is null)
{
return false;
}
else if(!from.IsGenericType)
{
return from.IsAssignableFrom(#this);
}
else if(!from.IsGenericTypeDefinition)
{
return from.IsAssignableFrom(#this);
}
else if(from.IsInterface)
{
foreach(Type #interface in #this.GetInterfaces())
{
if(#interface.IsGenericType && #interface.GetGenericTypeDefinition() == from)
{
return true;
}
}
}
if(#this.IsGenericType && #this.GetGenericTypeDefinition() == from)
{
return true;
}
return #this.BaseType?.ImplementsOrDerives(from) ?? false;
}
You need to compare the contained type. See: How to get the type of T from a member of a generic class or method?
In other words, I think you need to check whether the type being contained by the generic class is assignable rather than the generic class itself.
#konrad_ruldolph's answer is mostly correct, but it requires you to know the base type/interface is an open generic. I propose an improvement that combines a non-generic test with a loop to test for generic match.
public static class Ext
{
public static bool IsAssignableToGeneric(
this Type assignableFrom,
Type assignableTo)
{
bool IsType(Type comparand)
=> assignableTo.IsAssignableFrom(comparand)
|| (comparand.IsGenericType
&& comparand.GetGenericTypeDefinition() == assignableTo);
while (assignableFrom != null)
{
if (IsType(assignableFrom)
|| assignableFrom
.GetInterfaces()
.Any(IsType))
{
return true;
}
assignableFrom = assignableFrom.BaseType;
}
return false;
}
}
Creating an extension method and using link you can do this :
public static bool IsAssignableFromGenericInterface(this Type type, Type genericInterface) => type.GetInterfaces().Any(#interface => #interface.IsAssignableFrom(genericInterface));
I also would like to share my code with you. Here the generic arguments are checked for any compatibility and is working with interfaces.
public static bool IsAssignableToGeneric(this Type sourceType, Type targetType)
{
bool IsAssignable(Type comperand)
{
if (comperand.IsAssignableTo(targetType))
return true;
if (comperand.IsGenericType && targetType.IsGenericType && comperand.GetGenericTypeDefinition() == targetType.GetGenericTypeDefinition())
{
for (int i = 0; i < targetType.GenericTypeArguments.Length; i++)
{
Type comperandArgument = comperand.GenericTypeArguments[i];
Type targetArgument = targetType.GenericTypeArguments[i];
// suggestion for improvement: forward the type check recursivley also here
if (!comperandArgument.IsGenericTypeParameter && !targetArgument.IsGenericTypeParameter && !comperandArgument.IsAssignableTo(targetArgument))
return false;
}
return true;
}
return false;
}
if (IsAssignable(sourceType))
return true;
if (targetType.IsInterface && sourceType.GetInterfaces().Any(IsAssignable))
return true;
return false;
}
I had to compare equality of two two-dimensional arrays. I tried to use StructuralComparisons.StructuralEqualityComparer.Equals(obj1, obj2) and it works.
So now I wrote this method (in Helpers utility class):
public static bool Equals<T>(T obj1, T obj2)
{
return StructuralComparisons.StructuralEqualityComparer.Equals(obj1, obj2);
}
And a question: Which method should I use when I don't know whether Type T is struct (i.e. array) or class? Object.Equals or this one?
The answer is yes.
In such situations we can use StructuralComparisons comparer because it checks whether first object implements IStructuralEquatable and if it really implements, then it uses IStructuredEquatable.Equals() method, otherwise it uses obj1.Equals(obj2).
The implementation of this method is listed below
public new bool Equals(Object x, Object y) {
if (x != null) {
IStructuralEquatable seObj = x as IStructuralEquatable;
if (seObj != null){
return seObj.Equals(y, this);
}
if (y != null) {
return x.Equals(y);
} else {
return false;
}
}
if (y != null) return false;
return true;
}
I've defined a C# class with a string member. For all intents an purposes, think of this class as being a subclass of string (except that's not allowed). I'm using it to represent a strongly typed string field that matches a specific format (I've simplified this significantly).
public class field
{
private readonly string m_field;
public field(string init_value)
{
//Check the syntax for errors
if (CheckSyntax(init_value))
{
m_field = init_value;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
public override string ToString()
{
return m_field;
}
}
Now, I want to be able to compare this class directly to any other string (object or literal). Therefore, I implemented the following in the class:
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
return this.m_field == obj.ToString();
}
public override int GetHashCode()
{
return this.m_field.GetHashCode();
}
public static bool operator ==(field x, Object y)
{
if ((object)x == null && y == null)
{
return true;
}
else if ((object)x == null || y == null)
{
return false;
}
else
{
return (x.m_field == y.ToString());
}
}
public static bool operator !=(field x, Object y)
{
return !(x == y);
}
Now when I'm writing a unit test, depending on the order that I'm passing in the arguments to Assert.AreEqual, I get different results:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // PASSES
Assert.AreEqual(valid, target); // FAILS
I'm assuming this is because in the first assert, it's calling field.Equals() and in the second it's calling String.Equals(). Obviously I'm approaching this from the wrong angle. Can anyone give me some insight?
One other thing. I can't use a struct here (value type) because in my actual case I'm defining all this in a base class and inheriting from it.
Basically you can't do what you want to - there's no way you can make string recognise your class for equality purposes. You'll never be able to make it reflexive - you'll never be able to make it obey the contract of object.Equals.
I would personally try to redesign it so that you didn't have the validation as part of the type itself - make it part of the relevant properties of the business entities (or whatever they are).
This is described in detail in Effective Java as Item 8: Obey the general contract when overriding equals.
The equals method implements an equivalence relation.
It is Reflexive, Symmetric, Transitive, Consistent, and for any non-null reference x, x.equals(null) must return false. The example cited to break symmetry is similar to yours.
field class is aware of string class, but the built-in string class is not aware of field. This a one-way interoperability, and should be removed.
I would discourage anyone using your field class implicitly as a String, and force this type of usage:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.toString(), valid);
Assert.AreEqual(valid, target.toString());
Based on everyone's feedback, and my own needs, here's what I'm putting forward as a possible solution (I'm modifying the Equals method as follows):
public override bool Equals(Object obj)
{
if (obj == null)
{
return false;
}
field f = obj as field;
if (f != null)
{
return this == f;
}
else
{
return obj.Equals(this);
}
}
This seems to allow its correct use in dictionary and collection classes that rely on the Equals and GetHashCode methods for determining if the value already exists.
Also, now these both fail:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target, valid); // FAILS
Assert.AreEqual(valid, target); // FAILS
And these both pass:
string valid = "Some String";
field target = new field(valid);
Assert.AreEqual(target.ToString(), valid); // PASSES
Assert.AreEqual(valid, target.ToString()); // PASSES
And these both pass:
field f1 = new field("Some String");
field f2 = new field("Some String");
Assert.AreEqual(f1, f2); // PASSES
Assert.AreEqual(f2, f1); // PASSES
This is String#Equals
public override bool Equals(object obj)
{
string strB = obj as string;
if ((strB == null) && (this != null))
{
return false;
}
return EqualsHelper(this, strB);
}
Supplying an argument other than a String to String#Equals is going to return false. I'd suggest a 'rethink' to get around this.
I suggest using object.ReferenceEquals() if you are internally trying to validate whether x or y is null.
public static bool operator ==(field x, Object y)
{
if (object.ReferenceEquals(x, null) && object.ReferenceEquals(y, null))
{
return true;
}
else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
{
return false;
}
else
{
return (x.m_field == y.ToString());
}
}