.netdelegatesc#-9.0generic-constraintsc#-7.3

How to use "myFunc(Func<T>) where T : class" as well as "myFunc(Func<bool>)" implementing the same interface?


Background:

To get rid I of redundant code I am using strategy pattern. So I placed this (former) redundant code into context class of strategy pattern, where method IsSuccess ProcessApiMethod() will be called. Context class ist not where my problems lies, I just show it to you for completeness.

Difficulty:

So far solved:

My Problem - It lies where the difficutly is:

I miss something like the following ...

public interface ISuccessConverterStrategy<TOut> where Tout : (class || basicType)

But this would not make any sense:

public interface ISuccessConverterStrategy<TOut> where Tout : (class || (!class && !struct))

At the moment, no matter how I tried it, in my strategy classes compiler tells me "Interface member 'TOut SuccessConverter.ISuccessConverterStrategy.GetResult()' is not implemented" or he mentions another problem with usage of those generics.

This ist my full code for completeness:

public enum IsSuccess
{
   No = - 1,
   NotSupported = 0,
   Yes = 1,
   WithoutContent = 2
}

/// <sumary>
/// Interface of Strategy Pattern
/// </summary>
public interface ISuccessConverterStrategy
{
   IsSuccess ProcessApiMethod();
}
    
public interface ISuccessConverterStrategy<TOut> : ISuccessConverterStrategy
{
   /// <summary>
   /// After processing 
   /// Returns null, if processing method has no return value.
   /// Returns null, if something went wrong.
   /// Returns return value, otherwise.
   /// </summary>
   /// <typeparam name="TOut"></typeparam>
   /// <returns></returns>
   TOut GetResult<TOut>();
}

/// <summary>
/// Context class of strategy pattern.
/// </summary>
public class SilentSuccessConverter
{
   /// <summary>
   /// Silences API-method when called within strategy-method. Extends range of possible return values
   /// of type IsSuccess, e.g. by IsSuccess.NotSupported. Catches possible exception messages.
   /// </summary>
   /// <param name="strategy"></param>
   /// <param name="errorMessage"></param>
   /// <returns></returns>
   /// <exception cref="NullReferenceException"></exception>
   public IsSuccess Convert(ISuccessConverterStrategy strategy, out string errorMessage)
   {
       if (null == strategy)
       {
           throw new NullReferenceException(nameof(strategy));
       }

       errorMessage = "";

       try
       {
           return strategy.ProcessApiMethod();
       }
       catch (Exception e) when (e.Message.ToLower().Contains("not implemented"))
       {
           errorMessage = $" - Message: {e.Message} - Source: {e.Source}" + Environment.NewLine;
           return IsSuccess.NotSupported;
       }
       catch (Exception e)
       {
           errorMessage = $" - Message: {e.Message} - Source: {e.Source}" + Environment.NewLine;                
        
           if (e is NotImplementedException || e is NotSupportedException)
           {
               return IsSuccess.NotSupported;
           }
        
           return IsSuccess.No;
       }
   }   
}

public class FuncTOutClassStrategy<TOut> : ISuccessConverterStrategy<TOut> where TOut : class
{
   private Func<TOut> _apiMethod;
   private TOut _result;

   /// <summary>
   /// Holds a method without parameters, which returns an generic class object.
   /// </summary>
   /// <param name="apiMethod"></param>
   public void SetApiMethod(Func<TOut> apiMethod)
   {
      _apiMethod = apiMethod ?? throw new NullReferenceException(nameof(apiMethod));
   }
        
   public IsSuccess ProcessApiMethod()
   {
      _result = _apiMethod();
      return null != _result ? IsSuccess.Yes : IsSuccess.No; 
   }

   public TOut GetResult()
   {
      return _result;
   }
}

public class FuncIntOutStrategy<int> : ISuccessConverterStrategy<int> 
{
    private Func<int> _apiMethod;
    private int _result;

    /// <summary>
    /// Holds a method without parameters, which returns int.
    /// </summary>
    /// <param name="apiMethod"></param>
    public void SetApiMethod(Func<int> apiMethod)
    {
        _apiMethod = apiMethod ?? throw new NullReferenceException(nameof(apiMethod));
    }
    
    public IsSuccess ProcessApiMethod()
    {
        _result = _apiMethod();
        return _result > 0 ? IsSuccess.Yes : IsSuccess.No; 
    }

    public int GetResult()
    {
        return _result;
    }
}

public class ActionTInStrategy<TIn> : ISuccessConverterStrategy 
{
    private Action<TIn> _apiMethod;
    private TIn _input;

    /// <summary>
    /// Holds a void method that receives an object of type T.
    /// </summary>
    /// <param name="apiMethod"></param>
    /// <param name="input"></param>
    public void SetApiMethod(Action<TIn> apiMethod, TIn input)
    {
        _input = input;
        _apiMethod = apiMethod ?? throw new NullReferenceException(nameof(apiMethod));
    }

    public IsSuccess ProcessApiMethod()
    {
        _apiMethod(_input);
        return  IsSuccess.Yes; 
    }
}

public class ActionStrategy : ISuccessConverterStrategy
{
    private Action _apiMethod;

    /// <summary>
    /// Holds a void method without any parameters.
    /// </summary>
    /// <param name="apiMethod"></param>
    public void SetApiMethod(Action apiMethod)
    {
        _apiMethod = apiMethod ?? throw new NullReferenceException(nameof(apiMethod));
    }

    public IsSuccess ProcessApiMethod()
    {
        _apiMethod();
        return  IsSuccess.Yes; 
    }
}

Solution

  • Now I think I got it without errors. These are those code parts I modified:

    public interface ISuccessConverterStrategy<out TOut> : ISuccessConverterStrategy 
    {
       /// <summary>
       /// After processing 
       /// Returns null, if processing method has no return value.
       /// Returns null, if something went wrong.
       /// Returns return value, otherwise.
       /// </summary>
       /// <typeparam name="TOut"></typeparam>
       /// <returns></returns>
       TOut GetResult();
    }
    
    public class FuncBoolOutStrategy : ISuccessConverterStrategy<bool>
    {
        private Func<bool> _apiMethod;
        private bool _result;
    
        /// <summary>
        /// Holds a method without parameters, which returns bool.
        /// </summary>
        /// <param name="apiMethod"></param>
        public void SetApiMethod(Func<bool> apiMethod)
        {
            _apiMethod = apiMethod ?? throw new NullReferenceException(nameof(apiMethod));
        }
        
        public IsSuccess ProcessApiMethod()
        {
            _result = _apiMethod();
            return _result ? IsSuccess.Yes : IsSuccess.No; 
        }
    
        public bool GetResult()
        {
            return _result;
        }
    }
    
    public class FuncIntOutStrategy : ISuccessConverterStrategy<int> 
    {
        private Func<int> _apiMethod;
        private int _result;
    
        /// <summary>
        /// Holds a method without parameters, which returns int.
        /// </summary>
        /// <param name="apiMethod"></param>
        public void SetApiMethod(Func<int> apiMethod)
        {
            _apiMethod = apiMethod ?? throw new NullReferenceException(nameof(apiMethod));
        }
        
        public IsSuccess ProcessApiMethod()
        {
            _result = _apiMethod();
            return _result > 0 ? IsSuccess.Yes : IsSuccess.No; 
        }
    
        public int GetResult()
        {
            return _result;
        }
    }
    
    public class FuncTOutClassStrategy<TOut> : ISuccessConverterStrategy<TOut>  where TOut : class
    {
        private Func<TOut> _apiMethod;
        private TOut _result;
    
        /// <summary>
        /// Holds a method without parameters, which returns an generic class object.
        /// </summary>
        /// <param name="apiMethod"></param>
        public void SetApiMethod(Func<TOut> apiMethod)
        {
            _apiMethod = apiMethod ?? throw new NullReferenceException(nameof(apiMethod));
        }
        
        public IsSuccess ProcessApiMethod()
        {
            _result = _apiMethod();
            return null != _result ? IsSuccess.Yes : IsSuccess.No; 
        }
    
        public TOut GetResult()
        {
            return _result;
        }
    }
    

    Maybe you find something to improve, like necessary constraints or ... .