Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix CA2225 (OperatorOverloadsHaveNamedAlternates) when using generic class

I'm trying to resolve CA2225, which WARNs on ContrainedValue<T> below, with the following message: Provide a method named 'ToXXX' or 'FromXXX' as an alternate for operator 'ConstrainedValue<T>.implicit operator T(ConstrainedValue<T>)'.

I've also pasted PositiveInteger to illustrate a use-case for ConstrainedValue<T>. ConstrainedValue<T> enables derivatives to simply specify the constraint being applied to a value type, in the constructor. It seems like a pretty clean way to code this up. Is there any way to resolve the CA2225 warning, given that I'm dealing with a generic type? How can I provide an alternate operator?

  • Perhaps I could implement ToInt, ToDouble, etc. for all value types and have them throw if the from is not of the same type? But I think it's a bad practice to have the ToXXX method throw?
  • I could create a layer in-between ConstrainedValue<T> and PositiveInteger<T>, a ConstrainedInteger class. I could put ToInt() in that class. But it seems wrong to create a layer simply to satisfy CA2225 and I don't think the warning would go away on ConstrainedValue<T> and I would have to suppress that warning.

Code:

namespace OBeautifulCode.AutoFakeItEasy
 {
    using System.Diagnostics;

    using Conditions;

    /// <summary>
    /// Represents a constrained value.
    /// </summary>
    /// <typeparam name="T">The type of the constrained value.</typeparam>
    [DebuggerDisplay("{Value}")]
    public abstract class ConstrainedValue<T>
        where T : struct
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ConstrainedValue{T}"/> class.
        /// </summary>
        /// <param name="value">The value of the <see cref="ConstrainedValue{T}"/> instance.</param>
        protected ConstrainedValue(T value)
        {
            this.Value = value;
        }

        /// <summary>
        /// Gets the underlying value of the instance.
        /// </summary>
        public T Value { get; }

        /// <summary>
        /// Performs an implicit conversion from <see cref="ConstrainedValue{T}"/> to the underlying value type.
        /// </summary>
        /// <param name="from">The <see cref="ConstrainedValue{T}"/> to convert from.</param>
        /// <returns>
        /// The result of the conversion.
        /// </returns>
        public static implicit operator T(ConstrainedValue<T> from)
        {
            return from.Value;
        }            
    }

    /// <summary>
    /// Represents a positive integer.
    /// </summary>
    [DebuggerDisplay("{Value}")]
    public sealed class PositiveInteger : ConstrainedValue<int>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PositiveInteger"/> class.
        /// </summary>
        /// <param name="value">The value held by the <see cref="PositiveInteger"/> instance.</param>
        public PositiveInteger(int value)
            : base(value)
        {
            Condition.Requires(value, nameof(value)).IsGreaterThan(0);
        }
    }
}
like image 649
SFun28 Avatar asked Apr 25 '16 12:04

SFun28


1 Answers

You can make the code analysis happy by actually doing what it says:

Insert this in ConstrainedValue<T> and the warning will go away.

public T ToT()
{
  return this.Value;
}

Personally, I'd rather suppress the message, you already provide a method to get the value even if the language does not provide casting.

like image 188
nvoigt Avatar answered Oct 19 '22 18:10

nvoigt