Wednesday, 16 August 2017

Fun with C# 7.1; or "No extension methods for virtual tuples"

Madness? ...yeah, ok, kinda.

So there I was, looking at the release notes for Visual Studio 2017.3, and I saw it: "Implemented C# 7.1". Naturally, I had to try it. This... is a result of that.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharp7_1.Testing
{
    /// <summary>
    /// A Vector2 struct with two floating-point values.
    /// </summary>
    public struct Vector2
    {
        #region Private Fields

        /// <summary>
        /// The two floating-point values are stored as "x" and "y" in an
        /// anonymous tuple.
        /// </summary>
        private (float x, float y) values;

        #endregion Private Fields

        #region Public Constructors

        /// <summary>
        /// A Vector2 can be constructed out of an appropriate anonymous tuple.
        /// </summary>
        /// <param name="values"></param>
        public Vector2((float x, float y) values)
        {
            this.values = values;
        }

        /// <summary>
        /// A Vector2 can be constructed out of floating-point x and y values.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public Vector2(float x, float y)
            : this((x, y))
        {
        }

        #endregion Public Constructors

        #region Public Properties

        /// <summary>
        /// The X-value of the vector.
        /// </summary>
        public float X { get => values.x; set => values.x = value; }

        /// <summary>
        /// The Y-value of the vector.
        /// </summary>
        public float Y { get => values.y; set => values.y = value; }

        #endregion Public Properties

        #region Public Methods

        /// <summary>
        /// Implicit deconstruction to an anonymous tuple.
        /// </summary>
        /// <param name="value"></param>
        public static implicit operator (float x, float y) (Vector2 value)
        {
            return (x: value.X, y: value.Y);
        }

        /// <summary>
        /// Implicit conversion and construction from an an anonymous tuple.
        /// </summary>
        /// <param name="values"></param>
        public static implicit operator Vector2((float x, float y) values)
        {
            return new Vector2(values);
        }

        /// <summary>
        /// Thanks to our implicit conversions, we can add appropriate
        /// anonymous tuples to Vector2s, and vice-versa, and convert back to
        /// an anonymous tuple.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static Vector2 operator +(Vector2 left, Vector2 right)
        {
            return new Vector2(Extensions.Add(left.values, right.values));
        }

        public static (float x, float y) operator +((float x, float y) left, Vector2 right)
        {
            return Extensions.Add(left, right.values);
        }

        /// <summary>
        /// Returns the values of the Vector2 as a string.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return values.ToString();
        }

        #endregion Public Methods
    }

    public static class Extensions
    {
        #region Public Methods

        /// <summary>
        /// Unfortunately (or perhaps "unfortunately"), anonymous tuples do not
        /// support extension methods, much less extension operators.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static (float x, float y) Add((float x, float y) left, (float x, float y) right)
        {
            return
                (
                    x: left.x + right.x,
                    y: left.y + right.y
                );
        }

        #endregion Public Methods
    }

    internal class Program
    {
        #region Private Methods

        private static void Main(string[] args)
        {
            Console.WriteLine(Extensions.Add((1.5F, 2.5F), (2.0F, 2.5F)));

            Vector2 a = (2.5F, 1.5F);
            // We don't need to name these "x" and "y", but it helps keep track
            // of what they're for.
            var b = (x: 2.5F, y: 2.0F);

            Console.WriteLine(a + b);
            // This! Is! ...A very odd thing to do.
            Console.WriteLine((x: 2.5F, y: 1.5F) + (Vector2)(x: 2.5F, y: 2.0F));

            Console.ReadKey(true);
        }


        #endregion Private Methods
    }
}

No comments:

Post a Comment