I am trying to use C# 11 ref struct/fields to create a "ref tuple", that is a type that holds multiple refs.
ref struct RefTuple<T1, T2> {
public ref T1 Item1;
public ref T2 Item2;
public RefTuple(ref T1 item1, ref T2 item2)
{
Item1 = ref item1;
Item2 = ref item2;
}
public bool Equals(RefTuple<T1, T2> o) {
return Equals(Item1, o.Item1) && Equals(Item2, o.Item2);
}
public override bool Equals(object o) {
return o is RefTuple<T1, T2> rt && Equals(rt);
}
public override int GetHashCode() {
retrun HashCode.Combine(Item1?.GetHashCode() ?? 0, Item2?.GetHashCode() ?? 0);
}
public static bool operator ==(RefTuple<T1, T2> obj1, RefTuple<T1, T2> obj2) {
return obj1.Equals(obj2);
}
public static bool operator !=(RefTuple<T1, T2> obj1, RefTuple<T1, T2> obj2) {
return !obj1.Equals(obj2);
}
}
The idea is I can then initialize and pass an object of type RefTuple<T1, T2> to a function using a ref, and that function can then change Item1 etc which will modify the original ref used when initializing the RefValue.
Problems:
Equals(object o) above does not compile seems the is operator does not work with ref structs ? I am not sure how to correctly implement it.
Equals(object o) also shows "Nullability of type of parameter 'o' doesn't match overridden member" which I am not able to resolve. (code has nullability enabled)
Equals(RefTuple<T1, T2> o) is using object.Equals but I believe that may not be efficient as it may end up boxing. I tried instead Item1.Equals(o.Item1) && Item2.Equals(o.Item2) but then get the nullability warnings above.
There's some limits when using a ref struct, one of them is:
A ref struct can't be boxed to System.ValueType or System.Object.
The parameter o in Equals(object o) can never be the type of RefTuple, so you can simply return false.
The signature of this method in the base class is:
bool Equals(object? obj)
The overridden method need match it.
public override bool Equals(object? o) => false;
Even if you call Item1.Equals(o.Item1), you are still calling the method Equals(object), boxing cannot be ignored. The built-in ValueTuple class uses EqualityComparer<T1>.Default, maybe this is for reference.
public bool Equals(RefTuple<T1, T2> o) {
return EqualityComparer<T1>.Default.Equals(Item1, o.Item1)
&& EqualityComparer<T2>.Default.Equals(Item2, o.Item2);
}
Related
Following code won't compile:
class Test<T> where T : class, IComparable
{
public bool IsGreater(T t1, T t2)
{
return t1 > t2; // Cannot apply operator '>' to operands of type 'T' and 'T'
}
}
How I can make it work? I want it to work for T = int, double, DateTime, etc.
The IComparable interface that you have used here as the type constraint gives you a CompareTo method. So you can do something like this:
public bool IsGreater(T t1, T t2)
{
return t1.CompareTo(t2) > 0;
}
While currently you can't do exactly that (and you need to use solution by #DavidG) there is a preview feature you can enable called generic math which is based on static abstract interface members and ships with interface specially for that purpose:
[RequiresPreviewFeatures]
public interface IComparisonOperators<TSelf, TOther> : IComparable, IComparable<TOther>, IEqualityOperators<TSelf, TOther>, IEquatable<TOther> where TSelf : IComparisonOperators<TSelf, TOther>
{
static bool operator <(TSelf left, TOther right);
static bool operator <=(TSelf left, TOther right);
static bool operator >(TSelf left, TOther right);
static bool operator >=(TSelf left, TOther right);
}
So in future (or right now if you want to be an early adopter) you will be able to do (if no major overhaul comes for this preview):
class Test<T> where T : IComparisonOperators<T, T>
{
public bool IsGreater(T t1, T t2)
{
return t1 > t2;
}
}
Note that for Test to support int, double, DateTime, etc. you need to remove class constraint (with or without this preview feature).
You can use this to compare two values of same generic type.
public bool IsGreater(T t1, T t2)
{
return t1.CompareTo(t2) > 0;
}
Can I pass a method with an out parameter as a Func?
public IList<Foo> FindForBar(string bar, out int count) { }
// somewhere else
public IList<T> Find(Func<string, int, List<T>> listFunction) { }
Func needs a type so out won't compile there, and calling listFunction requires an int and won't allow an out in.
Is there a way to do this?
ref and out are not part of the type parameter definition so you can't use the built-in Func delegate to pass ref and out arguments. Of course, you can declare your own delegate if you want:
delegate V MyDelegate<T,U,V>(T input, out U output);
Why not create a class to encapsulate the results?
public class Result
{
public IList<Foo> List { get; set; }
public Int32 Count { get; set; }
}
The Func family of delegates (or Action for that matter) are nothing but simple delegate types declared like
//.NET 4 and above
public delegate TResult Func<out TResult>()
public delegate TResult Func<in T, out TResult>(T obj)
//.NET 3.5
public delegate TResult Func<T1, T2, TResult>(T1 obj1, T2 obj2)
public delegate TResult Func<T1, T2, T3, TResult>(T1 obj1, T2 obj2, T3 obj3)
etc. Delegates as such can have out/ref parameters, so in your case its only a matter of custom implementation by yourself as other answers have pointed out. As to why Microsoft did not pack this by default, think of the sheer number of combinations it would require.
delegate TResult Func<T1, T2, TResult>(T1 obj1, T2 obj2)
delegate TResult Func<T1, T2, TResult>(out T1 obj1, T2 obj2)
delegate TResult Func<T1, T2, TResult>(T1 obj1, out T2 obj2)
delegate TResult Func<T1, T2, TResult>(out T1 obj1, out T2 obj2)
for just two parameters. We have not even touched ref. It would actually be cumbersome and confusing for developers.
You could wrap it in a lambda/delegate/function/method that exposed the right interface and called FindForBar, but I suspect that FindForBar has count as an out parameter as a reason, so you'd need to be sure throwing that information away was ok/safe/desirable/had the right results (you'd need to be sure of this even if you could just directly pass in FindForBar).
So yay for Tuples! I know I can return multiple values from a method with tuples and name them so they don't have to be Item1, Item2, etc.
I can go var results = (result1: r1, result2: r2);
But how do you use tuple literals in an interface declaration such as this?
public interface IFoo {
(T1, T2) Bar();
}
Tried doing (result1: T1, result2: t2) Bar(); but it doesn't work. Is there a way of returning a named tuple from an interface method?
Your syntax for named tuples as a method return type is incorrect:
public interface IFoo {
(T1 result1, T2 result2) Bar();
}
Note: This is because your return type defines the type and name, while assigning names of a tuple literal defines the value and name.
// Method return syntax. Type & Name
public (bool success, T payload) TryGetPayload<T>() { }
// Literal assignment syntax. Name & Value
var result = (success: true, payload: t);
You can't use "literals" to any interface like that, unless as return values for concrete methods.
You can, however, use types. If your T1 and T2 are meant to be generic, you can use Ivan Stoev's comment on the question:
public interface IFoo<T1, T2> {
(T1, T2) Bar();
}
If your T1 and T2 are meant to be concrete, you can use them directly, as mentioned in Neil's answer:
public interface IFoo<T1, T2> {
(int, string) Bar();
}
You can also name them, if you want:
public interface IFoo<T1, T2> {
(T1 theLeft, T2 theRight) Bar();
}
or
public interface IFoo<T1, T2> {
(int theNumber, string theName) Bar();
}
I need a static function like this :
public static class Output
{
public static Tuple<T1, T2> Output(T1 out1, T2 out2)
{
return new Tuple<T1, T2>(out1,out2);
}
}
But the function has syntax errors. Anyone can help?
NOTE I mean :
retun Output("asd", 10);
return Output(myclassInstance, date);
... instead of below
return new Tuple<string, int>("asd", 10);
return new Tuple<Myclass, DateTime>(myclassInstance, date);
You need to make the method generic:
public static Tuple<T1, T2> YourMethod<T1, T2>(T1 out1, T2 out2)
{
return new Tuple<T1, T2>(out1,out2);
}
However... all of this is just the Tuple.Create method-group - so:
return YourMethod("asd", 10);
is the same as:
return Tuple.Create("asd", 10);
As a side note, you might want to consider ValueTuple<...> (https://www.nuget.org/packages/System.ValueTuple/). In addition to being more efficient, there is inbuilt language support in latest language versions. See https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/#user-content-tuples
The method must not have the same name as the class.
The method needs to be generic (have type parameters)
public static Tuple<T1, T2> Output<T1, T2>(T1 out1, T2 out2)
Since you're using C#7 (according to your tags), you might want to consider doing it like this:
public static (T1 out1, T2 out2) Output<T1, T2>(T1 out1, T2 out2)
{
return (out1, out2);
}
In this case it seems likely that you can give the tuple elements better names than out1 and out2.
However, at this point it seems even more likely that you could forego this wrapper method altogether, and just use the C#7 "Tuple Return" language feature directly where you need it.
There is such builtin function (https://referencesource.microsoft.com/#mscorlib/system/tuple.cs,9124c4bea9ab0199):
public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2)
{
return new Tuple<T1, T2>(item1, item2);
}
So I have a class which overrides Equals(object obj) and GetHashCode() along with implementing IEquatable. To make working with this type a little more natural when checking for equality I thought, heck, I'd overload the equality operator and inequality operator, no worries...
Uh oh, worries... consider the following - where both myType instances are NOT null:
if (myType != container.myType) //NullReferenceException
{
//never get here
}
//never get here either
Now, container is just another class to hold an instance of myType among other things which is used for caching items.
Here's the actual (relevant) code from myType:
public class MyType : IEquatable<MyType>
{
public static bool operator ==(MyType myTypeA, MyType myTypeB)
{
return myTypeA.Equals(myTypeB);
}
public static bool operator !=(MyType myTypeA, MyType myTypeB)
{
return !(myTypeA == myTypeB);
}
public override bool Equals(object obj)
{
if (obj != null && obj is MyType)
{
return Equals((MyType)obj);
}
return false;
}
public bool Equals(MyType other)
{
if (other != null)
{
return other.ToString() == ToString();
}
return false;
}
}
Any experience on this front?
Thanks.
Couple of pointers -
If you've overridden == and != on classes, make sure to use ReferenceEquals to check for null inside the overload implementations rather than ==, as that will call your overloaded operator and either go into a loop or try to call Equals on a null this reference, which is probably what is happening here.
Don't override == and != on classes. Those operators are meant for value equality, and classes aren't really designed to have value equality. Either remove the operator overloads, or make MyType a struct.
Tricky one... the problem is that you use the equality operator inside the Equal override as follows:
public bool Equals(MyType other)
{
if (other != null)
It goes to your overloaded != operator, which in turn goes to your == operator, which trying to do null.Equals...
As the others have stated you need to be carefull checking for nulls as it will call your equality function again, normally resulting in a StackOverflowException.
When I use the IEquatable interface on classes I normally use the following code:
public override bool Equals(object obj)
{
// If obj isn't MyType then 'as' will pass in null
return this.Equals(obj as MyType);
}
public bool Equals(MyType other)
{
if (object.ReferenceEquals(other, null))
{
return false;
}
// Actual comparison code here
return other.ToString() == this.ToString();
}