Can you compare T elements? - c#

I'm making a function that can take in T elements and compare them. However, whenever I try to compare T elements, I get a CS0019. It says
Operator '==' cannot be applied to operands of type 'T' and 'T'
public static void Test<T>(T test1, T test2)
{
if(test1 == test2)
{
...
}
}
I thought it would be a common mistake, but had trouble finding ways to fix it. Just to help you, I found a Comparer<T> thing but it didn't look to helpful. If you think you'll understand it, you can try. Thanking in advance!

In order to use == or != it has to be implemented as an operator on the type. When using generics, the compiler doesn't know whether those operators have been implemented hence the compiler error, therefore you have to fall back to using Equals; for example:
public static void Test<T>(T test1, T test2)
{
if(test1.Equals(test2))
{
...
}
}
Note that the above will only correctly determine equality if Equals on type T has been implemented correctly.

Related

Performance of == vs Equals in generic C# class

For some reason C# does not allow == operator use in generic classes like here:
class Mine<T> where T : struct
{
T val;
public T Value
{
set
{
if (val == value) // Operator '==' cannot be applied to operands of type T and T happens here
{
// .. do something ...
}
}
}
}
If I replace == with val.Equals(value) I have code that works as expected but if I look at bytecode it looks much more complicated.
A very simple test comparing int variables in the loop using == and Equals() showed that Equals() version was two times slower than "==" version.
I wonder if there is a way to compare primitive value types in generic classes that would be as fast as == operator.
Any ideas welcome.
Edit:
I got lost between timers. Performance difference is not as dramatic. Here are my latest results:
== operator 1974380 ticks
Equals() 1976358 ticks
== operator in another static function 1974604 ticks
EqualityComparer<int>.Default... 32486695 ticks
In short: Equals() is good enough.
The reason being that == defaults to reference equality and that makes no sense for value types, the answer will always be false. Because there is no language mechanism to constrain generic types based upon static methods, the compiler simply disallows this as it can't verify that T really has an overloaded == operator.
On the other hand if you constraint T to class it will compile just fine because reference equality does make sense for reference types.
The solution is of course IEquatable<T>; in any sanely implemented struct IEquatable<T>.Equals(T t) will give you value equality semantics and == should behave consistently.
And answering your question, no there is not. If you really need the speed of int == int you will need to implement a non generic specialized class.
If you are allowed to add the IEquatable<T> constraint to the class, then you can use the IEquatable<T>.Equals(T other) method declared in that interface:
class Mine<T> where T : struct, IEquatable<T>
{
T val;
public T Value
{
set
{
if (val.Equals(value)) //
{
// .. do something ...
}
}
}
}

Is there a way to tell which exact enum I'm working with at run-time?

I have two enums and a generic method. The generic type T could be either one of the enums.
public enum myEnumA
{
a,
b
}
public enum myEnumB
{
c,
d
}
public void myMethod<T>()
{
if (typeof(T) is myEnumA)
{
//do something
}
else if (typeof (T) is myEnumB)
{
//do something else
}
}
The compiler tells me "the given expression is never of the provided type" regarding the if check.
Is there a way to tell which exact enum it is at run time?
You want:
if (typeof(T) == typeof(MyEnumA))
to compare the types. The is operator is for testing whether a value is of a particular type.
Note that having to test for particular types within a generic method suggests that it might not really be very generic after all - consider using overloads or just entirely separate methods instead.
Because typeof returns a Type instance and that will never be compatible with your enum types. So is will return always false. Instead you need
if (typeof(T) == typeof(myEnumA))
You can do
if (typeof(T) == typeof(myEnumA))
Your types are enums, which are sealed. Were your types not sealed, you might need to use the IsAssignableFrom method, to check for subclassing, e.g.:
if (typeof(BaseTypeA).IsAssignableFrom(typeof(T))

Why type inference and implicit operator is not working in the following situations?

I will try to explain my question on an example:
class V<T>
{
public readonly Func<T> Get;
public readonly bool IsConstant;
V(Func<T> get, bool isConstant)
{
Get = get;
IsConstant = isConstant;
}
public static implicit operator V<T>(T value)
{
return new V<T>(() => value, true);
}
public static implicit operator V<T>(Func<T> getter)
{
return new V<T>(getter, false);
}
}
void DoSomething<T>(V<T> v)
{
//...
}
void Main()
{
DoSomething<string>("test"); // (1) type inference is not working
DoSomething<string>((V<string>)(() => "test")); // (2) implicit operator does not work
}
In the method Main, I have two situations:
I have to explicitly specify the generic argument <string> to the method DoSomething.
Here, I have to add the explicit cast (V<string>), the implicit operator does not seem to work.
Why is this required? What the alternatives that the compiler is considering, so it cannot choose the correct way?
Your second question is why the implicit conversion from ()=>"" to V<string> does not succeed, even though ()=>"" is convertible to Func<string> and Func<string> is convertible to V<string>.
Again, I do not know how to answer "why not?" questions but I do know how to answer the question "what line of the specification indicates that the compiler ought to reject this code?" The relevant line is:
First, if required, performing a standard conversion from the source type to the operand type of the user-defined or lifted conversion operator.
Note that there is a small error here; this should say performing a standard conversion from the source expression. The source expression may not have a type. I believe I gave that note to the spec maintainer before I left the team, so hopefully this will get fixed in the next edition.
Anyway, it should now be clear what is going on here. There is no standard conversion from a lambda to a delegate type, and therefore the user-defined conversion is classified as inapplicable by the conversion resolution algorithm.
I assume that your code intended to call DoSomething, not DumpValue.
Your question is, first, why does
DoSomething("");
not infer that the call is intended to be
DoSomething<string>((V<string>)"");
correct?
"Why" questions are very difficult to answer, and "why not?" questions are even harder. Instead, I'll answer a question that can be answered: what line of the specification justifies this behaviour?
Overload resolution works like this: if the method group contains a generic method but no generic method type arguments were provided then type inference attempts to infer the type arguments. If type arguments cannot be inferred then the method is removed from consideration for overload resolution. In your case, since there is only one method in the method group, removing it will cause overload resolution to fail.
Why then does type inference fail? T cannot be inferred because the controlling line of the specification is:
if V is a constructed type C<V1…Vk> and there is a unique set of types U1…Uk such that a standard implicit conversion exists from U to C<U1…Uk> then an exact inference is made from each Ui for the corresponding Vi.
There is no standard implicit conversion from string to V<string>. That is a user-defined implicit conversion.
Therefore type inference fails.
I'll answer your second question in a second answer. In general it is a bad idea to ask two questions in one. Post two questions when you have two questions.
I know your question was "why type inference isn't working", but I just thought that I'd address your statement at the end about the 2 alternatives. In this case, I believe a better alternative to the implicit conversion (eww do I hate those) is a static factory method. IMO, the syntax will be better when you call DumpValue.
static class VFactory
{
public static V<T> Create<T>(T value)
{
return new V<T>(() => value, true);
}
public static V<T> Create<T>(Func<T> getter)
{
return new V<T>(getter, false);
}
}
class V<T>
{
public readonly Func<T> Get;
public readonly bool IsConstant;
internal V(Func<T> get, bool isConstant)
{
Get = get;
IsConstant = isConstant;
}
}
void DumpValue<T>(V<T> v)
{
//...
}
void Main()
{
DumpValue(VFactory.Create("test"));
DumpValue(VFactory.Create(() => "test"));
}

What type should an overload of the negation operator (!) have?

If i overload the ! operator in a class, what type should it return? In a book I found this (partial listing):
public class MyType {
public int IntField { get; set; }
public MyType(int intField) {
IntField = intField;
}
public static bool operator !(MyType mt) {
return (mt.IntField <= 0);
}
It does compile, but I would expect the ! operator to return a MyType instance, something like
public static MyType operator !(MyType mt) {
var result = new MyType(-mt.IntField);
return result;
}
In fact, I would expect the compiler to demand that the ! operator returns a MyType. But it doesn't.
So... why doesn't the return type of the ! operator have to be the containing type? You do have to make the return type of ++ or -- be the containing type.
Suppose I asked you "what is the return type of concatenation"? What would you say? Probably you'd turn around and ask "concatenation of what?" Catenation is defined on chars, strings, sequences, catenable deques, languages, discrete finite automata and a thousand other things, so the type returned is determined by the type of the arguments. But typically the type of a concatenation is the type of the arguments. Not always; the concatenation of two chars is a string, for example. But typically.
Similarly, the question "what is the type of the ! operator?" depends entirely on what is being negated, and you haven't said what you are negating. Typically the negation of a T is another T, but it need not be.
I suspect you are not asking the right question. I think the question you ought to ask is "what is a realistic scenario in which you would overload the ! operator?" The example you give from the book is terrible; it does not motivate why the author of the code is overriding the operator at all.
Here's an more realistic example. Suppose we lived in a world without nullable types. You might decide to have three-valued logic:
sealed class MyBool
{
private MyBool() {} // No one creates it!
public static MyBool True = new MyBool();
public static MyBool False = new MyBool();
public static MyBool Unknown = new MyBool();
OK, what's the rule for negation of MyBool? True becomes False, False becomes True, Unknown stays Unknown:
public static MyBool operator !(MyBool b)
{
if (b == True) return False;
if (b == False) return True;
return Unknown;
}
In this scenario, the type of the ! operator is MyBool.
Of course, since C# 2.0 we have three-valued logic in the form of Nullable<bool>, but you might want more complex kinds of logic (or you might be writing C# 1.0 code).
It is harder to come up with reasonable examples of situations in which negation of a Foo results in a Bar; some sort of "monadic" workflow objects might be a possible situation -- where the negation of an object of a given type is an object of a different type that represents the deferred execution of the negation.

Comparison of a generic type with its default value, without a generic class constraint, gives a compile time error

I just ran into this situation and i thought it was a nice opportunity to use the default keyword. But it doesn't compile and i can't think of why. The example below illustrates my problem:
public class Test<TDataSource>
{
public IQueryable<TDataSource> DataSource { get; set; }
public bool GetOneOrDefaultResult()
{
var result = DataSource.SingleOrDefault();
return result != default(TDataSource);
}
}
You'll get an error on line 8 ("Operator '==' cannot be applied to operands of type 'TDataSource' and 'TDataSource'."). I thought using the default keyword would eliminate any comparison problems between reference types and value types.
Adding a generic constraint limiting TDataSource to reference types makes this piece of code compile.
Can somebody explain why the compiler won't fix this for me? Is it just not smart enough to see this would work?
This is related:
Can't operator == be applied to generic types in C#?
[Edit]
SLaks answer gave me some inspiration, the '==' operator won't work, but the Equals function should.
public class Test<TDataSource>
{
public IQueryable<TDataSource> DataSource { get; set; }
public bool GetOneOrDefaultResult()
{
var result = DataSource.SingleOrDefault();
return result.Equals(default(TDataSource));
}
}
This compiles would this function correctly?
You cannot assume that every value type overrides the == operator. (And even if they did, there would be no way to call it using generics; it's a static method)
Instead, you should write
return !(ReferenceEquals((object)result, (object)default(TDataSource))
|| result.Equals(default(TDataSource)));
If result is null (and a reference type), the ReferenceEquals call will return true, so Equals won't be called and won't throw a NullReferenceException.
If TDataSource is a value type, the ReferenceEquals will compare two different boxed references (which may happen to contain the same value, but will still be different), so it will pass on to the Equals call.

Categories

Resources