Consider the following code:
class CustomClass
{
public CustomClass(string value)
{ m_value = value; }
public static bool operator ==(CustomClass a, CustomClass b)
{ return a.m_value == b.m_value; }
public static bool operator !=(CustomClass a, CustomClass b)
{ return a.m_value != b.m_value; }
public override bool Equals(object o)
{ return m_value == (o as CustomClass).m_value; }
public override int GetHashCode()
{ return 0; /* not needed */ }
string m_value;
}
class G
{
public static bool enericFunction1<T>(T a1, T a2) where T : class
{ return a1.Equals(a2); }
public static bool enericFunction2<T>(T a1, T a2) where T : class
{ return a1==a2; }
}
Now when I call both generic functions, one succeeds and one fails:
var a = new CustomClass("same value");
var b = new CustomClass("same value");
Debug.Assert(G.enericFunction1(a, b)); // Succeeds
Debug.Assert(G.enericFunction2(a, b)); // Fails
Apparently, G.enericFunction2 executes the default operator== implementation instead of my override. Can anybody explain why this happens?
From Constraints on Type Parameters (C# Programming Guide):
When applying the where T : class constraint, avoid the == and != operators on the type parameter because these operators will test for reference identity only, not for value equality. This is the case even if these operators are overloaded in a type that is used as an argument. (...) The reason for this behavior is that, at compile time, the compiler only knows that T is a reference type, and therefore must use the default operators that are valid for all reference types.
If I change the enericFunction2 to:
public static bool enericFunction2<T>(T a1, T a2) where T : class
{
object aa = a1;
CustomClass obj1 = (CustomClass)aa;
object bb = a2;
CustomClass obj2 = (CustomClass)bb;
return obj1 == obj2;
}
Then everything works fine. But I am afraid I can't explain it. I mean a1 and a2 know their type. Why a cast is needed to the CustomClass, so the operators are called?
Related
I am trying to write an Alias class which enables me to:
int n = new Count(1);
That is, it encapsulates an int in this case as a Count, which gives some type safety and domain meaning, while it automatically converts back to the underlying type.
With non-nullable reference types, I have another issue. I cannot figure out how to handle both of these scenarios at the same time:
int someCount = new Count(1);
Count? nothing = null;
int? noCount = nothing;
This happens because I have types like this:
record Device(Count Cpu, Count? Cores); // Contrived example
Seems like the problem is I cannot overload an operator with both nullable and non-nullable version of the same type:
record Alias<T>(T Value)
{
public static implicit operator T(Alias a) => a.Value;
public static implicit operator T?(Alias? a) => null;
}
record Count : Alias<int> { /**/ }
The point is, if I have a null, I want it converted to null of the target type.
If you don't have any Aliases that wraps reference types, then I think the best thing to do here is to just limit T here to structs. After that, T, and T? become distinct types, allowing you to create two operators:
record Alias<T>(T Value) where T: struct
{
public static implicit operator T?(Alias2<T>? a) => a?.Value;
public static implicit operator T(Alias2<T> a) => a.Value;
}
If you also need to wrap reference types as well, you could consider adding another Alias type that works just for reference types:
record AliasClass<T>(T Value) where T: class
{
[return: NotNullIfNotNull("a")]
public static implicit operator T?(AliasClass<T>? a) => a?.Value;
}
record AliasStruct<T>(T Value) where T: struct
{
public static implicit operator T?(AliasStruct<T>? a) => a?.Value;
public static implicit operator T(AliasStruct<T> a) => a.Value;
}
Then you can have for example:
record Count(int Value) : AliasStruct<int>(Value) { /**/ }
record StringWrapper(string Value) : AliasClass<string>(Value) { /**/ }
As commented, it's not possible to overload an operator with both nullable and non-nullable generic types.
I kind of solved it with extension methods:
public static class AliasClass
{
public static V? Unwrap<V, A>(this Alias<V, A>? a)
where A : Alias<V, A> where V : class => a?.Value;
}
public static class AliasStruct
{
public static V? Unwrap<V, A>(this Alias<V, A>? a)
where A : Alias<V, A> where V : struct => a?.Value;
}
[SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")]
public abstract record Alias<V, A> where A : Alias<V, A>
{
protected Alias(V value)
{
Value = value;
EnsureValid(Value);
}
public V Value { get; }
protected virtual void EnsureValid(V value) { }
public override sealed string ToString() => Value?.ToString() ?? "";
public static implicit operator V(Alias<V, A> a) => a.Value;
}
Usage:
int a = new Count(1);
int? n = new Count(2).Unwrap();
The symmetry, sadly, is broken. I can't find a way to implement Unwrap() for the non-nullable case.
I'm writing an abstract wrapper for enum in C # (I want something like enum in Vala). My code is:
public abstract class Wraper<T, TE>
where T : Wraper<T, TE>, new()
{
public TE Value;
public static implicit operator T(TE value)
{
return new T() { Value = value };
}
public static implicit operator TE(T value)
{
return value.Value;
}
}
I want to do with something like this:
public enum EFoo { A, B, C, D, E};
public class Foo : Wraper<Foo, EFoo>
{
public bool IsBla
{
get { return Value == EFoo.A || Value == EFoo.E; }
}
}
...
Foo foo = EFoo.A;
bool bla = foo.IsBla;
But the code does not compile because C # does not allow both generic parameter,. When compile reports an error:
User-defined conversion must convert to or from the enclosing type
On line
public static implicit operator T(TE value)
Is there any work around or in C# is not so simple?
The implicit operator must convert either from or to the class you are defining it in. Since you are trying to define it in the abstract base class, it can only convert from or to that class.
The line Foo foo = EFoo.A; does not work because you can't define that conversion in your abstract class, you can only define it in your Foo class.
If you changed your code, as I did below, to convert from/to the base class, the implicit conversions work but you don't get the result you want. Wraper<Foo,EFoo> foo2 = EFoo.A; works because it can be defined in the base class.
Your best bet is probably to use the initializer syntax when creating your foo Foo foo = new Foo { Value = EFoo.A }; or to create some generic conversion functions.
public abstract class Wraper<T, TE>
where T : Wraper<T, TE>, new()
{
public TE Value;
public static implicit operator TE(Wraper<T, TE> value)
{
return value.Value;
}
public static implicit operator Wraper<T, TE>(TE value)
{
return new T { Value = value };
}
}
public enum EFoo
{
A,
B,
C,
D,
E
}
public class Foo : Wraper<Foo, EFoo>
{
public bool IsBla
{
get
{
return Value == EFoo.A || Value == EFoo.E;
}
}
}
I have some custom wrapper types defined with an explicit cast operation:
private class A
{
private readonly int _value;
public A(int value)
{
_value = value;
}
public int Value { get { return _value; } }
}
private class B
{
private readonly int _value;
private B(int value)
{
_value = value;
}
public int Value { get { return _value; } }
public static explicit operator B(A value)
{
return new B(value.Value);
}
}
The following works just fine:
B n = (B)new A(5);
This does not:
B n = (B)(object)new A(5);
// Throws System.InvalidCastException:
// Unable to cast object of type 'A' to type 'B'
...what's going on here?
I'm constrained to have the cast to object first - the actual cast is being performed elsewhere in generic library code (so it's of the form return (TOutput)(object)input;, and dynamic is not an option)
The reason the first code snippet works is that what you see is not a "straight" cast: it is an invocation of an explicit conversion operator. This operation is well-defined, and works exactly the way it is coded: a brand-new object B is constructed from A's value. C# has enough information to determine that the explicit operator is to be invoked, because new A(5) type matches the type of operator B(A value) parameter.
Once you add a cast to object, however, the compiler does a true cast. C# no longer has an applicable user-defined conversion operator, so it does the type check, determines that A is not convertible to B, and throws an exception.
There is a way to make it work without dynamic by building and compiling a dynamic lambda, like this:
private static B ConvertFromObject(object a) {
if (a == null) return null;
var p = Expression.Parameter(typeof(object));
var c1 = Expression.Convert(p, a.GetType());
var c2 = Expression.Convert(c1, typeof(B));
var e = (Func<object,B>)Expression.Lambda(c2, p).Compile();
return e(a);
}
Demo.
You could cache compiled lambdas by a's runtime type to save on the costs of compiling a new expression each time you run the method.
I am making a comparer for set operation on various types.
So I have a generic class
public class Comparer<T, Tid>
...
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (Tid)xid == (Tid)yid;
}
Where m_idfunc is a lambda passed in to the Comparer constructor, it is
Func<T,Tid>
I create a comparer with Tid = string. I get in the equals function xid = string1, yid = string2
If string1 and string 2 are the same ("foo" and "foo" say)
xid == yid
yields false
(Tid)xid == (Tid)yid
also yields false (it should not be necessary - I was just getting desperate)
heres my immediate window - paused on the return xid == yid line
yid.GetType() == typeof(string)
true
xid.GetType() == typeof(string)
true
xid==yid
false
(string)xid==(string)yid
true
xid.Equals(yid)
true
Whats going on?
What’s interesting about this is that it might just work the way you want it to. Here’s an example:
using System;
using System.Text;
namespace ConsoleApplication1 {
class Program {
public static void Main() {
string myString = "1";
object objectString = "1";
string myCopiedString = string.Copy(myString);
string internedString = string.Intern(myCopiedString);
Console.WriteLine(myString); //1
Console.WriteLine(objectString); //1
Console.WriteLine(myCopiedString); //1
Console.WriteLine(internedString); //1
Console.Write(objectString == myString); //true
Console.Write(objectString == "1"); //true
Console.Write(objectString == myCopiedString); //!!!FALSE!!!!
Console.Write(objectString == internedString); //true
Console.Write(objectString == SomeMethod()); //!!!FALSE!!!
Console.Write(objectString == SomeOtherMethod()); //true
}
public static string SomeMethod() {
StringBuilder sb = new StringBuilder();
return sb.Append("1").ToString();
}
public static string SomeOtherMethod() {
return "1".ToString();
}
}
}
The reason why it might work is due to string interning. So, this is definitely one to watch out for, because it can actually work when you test it, but depending on the implementation, it might suddenly break.
In your case, you need to determine whether you care about Reference equality or "value" equality. == is reference equality, which again, depending on whether or not the string is interned may be true. I suspect you actually want to use EqualityComparer<T>.Default.Equals in your function.
If you run open this in VS you'll see the compiler warning: “Possible unintended reference comparison; to get a value comparison, cast the left hand side to type 'string'”. In your case however, the compiler can't warn you, because as far as it knows, the types are objects, it doesn't know that one or both are string.
My initial assumption was that because it's generics, it can't do a reference to value conversion behind the scenes that it does for strings. I wanted to put together an example that supported this. :)
I had to make a few assumptions to put something together for this so my example may not be 100% on. (code I used is at the bottom)
I wasn't able to get anything to compile when I just had
class Comparer<T, TId>
{
private readonly Func<T, TId> m_idfunc;
public Comparer(Func<T, TId> idFunc)
{
m_idfunc = idFunc;
}
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (TId)xid == (TId)yid;
}
}
I found https://stackoverflow.com/a/390919/156708 and modifed the class declaration to be
class Comparer<T, TId> where TId : class
and it compiled. Step 1.
I set up the Equals function as
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (TId)xid == (TId)yid;
}
And the result is False (see full code for generation of value in xid|yid). Fitting my assumption that Generics has a hand in this. Not enough yet, need to see what happens if the Generics aspect is removed.
Changing the Comparer class to be
class Comparer<T>
{
private readonly Func<T, string> m_idfunc;
public Comparer(Func<T, string> idFunc)
{
m_idfunc = idFunc;
}
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return xid == yid;
}
}
returns True.
I'm not 100% on this, but my assumption is based on the fact that the == operator of the string class does a value check instead of a reference check. When using generics, it is likely setting up to only do reference checks (haven't dug into the IL to see what it's doing there), and if the string locations in memory are not the same then it will return false. (I'm kinda winging the details, as I don't know them prezactly, just a working hypothesis that seems to be working)
My complete sample code with generics is below.
using System;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var compare = new Comparer<Example, string>(example => example.id(example));
var ex1 = new Example();
var ex2 = new Example();
Console.WriteLine(compare.Equals(ex1, ex2));
Console.ReadLine();
}
class Example
{
public string id(Example example)
{
return new string(new [] {'f', 'o', 'o'});
}
}
class Comparer<T, TId> where TId : class
{
private readonly Func<T, TId> m_idfunc;
public Comparer(Func<T, TId> idFunc)
{
m_idfunc = idFunc;
}
public bool Equals(T x, T y)
{
var xid = m_idfunc(x);
var yid = m_idfunc(y);
return (TId)xid == (TId)yid;
}
}
}
}
Hope that helps... and that I'm not terribly wrong on my reasoning. :)
I think, it is more correct to use EqualityComparer<TId> inside Comparer<T, Tid>. Besides, instead of delegate I would use interface to get identifiers:
interface IObjectWithId<T>
{
T Id { get; }
}
class IdEqualityComparer<T, TId> : EqualityComparer<T>
where T : IObjectWithId<TId>
{
public override bool Equals(T x, T y)
{
return EqualityComparer<TId>.Default.Equals(x.Id, y.Id);
}
public override int GetHashCode(T obj)
{
return EqualityComparer<TId>.Default.GetHashCode(obj.Id);
}
}
class A : IObjectWithId<string>
{
public string Id { get; set; }
}
Usage:
var a = new A { Id = "foo" };
var b = new A { Id = "foo" };
var c = new A { Id = "bar" };
var comparer = new IdEqualityComparer<A, string>();
Console.WriteLine(comparer.Equals(a, b)); // true
Console.WriteLine(comparer.Equals(a, c)); // false
The C operator "==" has two very different meanings. It can either invoke a type-specific overloaded equality-operator method if the compiler can statically determine that such a method is applicable to the operand types, or it can perform a reference comparison between the operands, if both operands are known to be reference types and there may exist an object which could be referred to by both operands. For most types, only one type of comparison would be possible; value types do not support reference comparison, and most reference types do not overload the equality operator. There is, however, a common class which would support both types of comparison: System.String.
The vb.net language avoids ambiguity here by only allowing the = operator to be used on types which overload it. For reference comparisons, the Is operator is required. If one were to attempt to write your code in vb.net, the = operator would not be permitted on class-constrained generics. One could use the Is operator, but it would check for reference equality regardless of whether the operands overload =.
As it is, in C#, assuming you have a class constraint on your generic type (the == operator won't work without it), the compiler can only use an overloaded equality operator on a generic type if the type is constrained to one for which the operator is overloaded. Since you don't constrain your generic type parameter to string (indeed, since string is sealed, the compiler won't allow such a constraint) there's no way the compiler can use the string overload of the equality operator. Thus, it uses the version of the equality operator that it knows is available on a class-constrained generic--reference equality (equivalent to the Is operator in vb.net).
I'm sure this is a stupid question, but why does the following code not call the explicit operator for the cast on the child class MyBool?
public class DataType
{
public static explicit operator bool(DataType D)
{
return false;
}
public static explicit operator DataType(bool B)
{
return new DataType();
}
}
public class MyBool : DataType
{
public bool Value;
public MyBool()
{
Value = false;
}
public static explicit operator bool(MyBool B)
{
return B.Value;
}
public static explicit operator MyBool(bool B)
{
return new MyBool() { Value = B };
}
}
then:
List<DataType> Types = new List<DataType>();
Types.Add(new MyBool() { Value = true });
Types.Add(new MyBool() { Value = false });
foreach (DataType T in Types)
{
bool Value = (bool)T;
MessageBox.Show(Value.ToString());
}
Produces the output: false, false
Is my only option to write functions on each class to take the place of the explicit operator functions?
why does the following code not call the explicit operator for the cast on the child class MyBool?
Because the operator functions are static, hence also non-virtual and thus their target is resolved at compile time rather than runtime. This is the expected behaviour.
If you want to have polymorphic conversion operators you can call virtual functions inside the operators:
public abstract class DataType
{
public static explicit operator bool(DataType D)
{
return D.DoCastToBool();
}
public static explicit operator DataType(bool B)
{
// We haven’t got an instance of our class here.
// You can use a factory method pattern to emulate virtual constructors.
}
protected abstract bool DoCastToBool();
}
Operators are overloaded rather than overridden - in other words, the choice about which implementation to use is made at compile-time. The compiler only knows about T as DataType, so it calls the operator in DataType.
One option would be to remove the operator from MyBool, but add a virtual method in DataType, allowing for polymorphic behaviour:
public class DataType
{
public static explicit operator bool(DataType D)
{
// TODO: Decide how you want to handle null references
return D.ToBoolean();
}
protected virtual bool ToBoolean()
{
return false;
}
}
public class MyBool : DataType
{
// ...
protected override bool ToBoolean()
{
return Value;
}
}
Note that this won't work for the conversion from bool to a DataType, as in that case we don't have any information about which subtype of DataType you actually want to create.
(Side-note: your code would be easier to follow if you used the normal .NET naming conventions.)
Here's a garbage solution for you:
replace: bool Value = (bool)T;
with: bool Value = (bool)(T as MyBool);