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:
- Method ProcessApiMethod() shall return null if TOut is a class.
- But TOut could be a basic type like int or bool, too.
So far solved:
- Further, some API-methods will have return type void. This is why I implemented two different interfaces.
My Problem - It lies where the difficutly is:
- Does C# allow alternate generic constraints?
- And how to just allow basic types as possible types of T?
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))
- If "No" is your answer to my first question: How can I solve this problem?
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;
}
}
CodePudding user response:
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 ... .