将方法转换为通用和非通用类

时间:2018-07-03 14:01:59

标签: c# generics

我想具体点。我有一个名为Result的类,以及一个从其派生的类Result

public class Result
{
    public string Message { get; set; }

    public bool Success { get; set; }
}

public class Result<T> : Result
{
    public T Data { get; set; }
}

这些类在方法中用作返回类型。为此,我制作了这个辅助类:

public class ResultManager
{
    /// <summary>
    /// Returns a success action
    /// </summary>
    public static Result Success()
    {
        var result = new Result { Success = true };

        return result;
    }

    /// <summary>
    /// Returns a success action
    /// </summary>
    /// <typeparam name="T">The object type to return with the result</typeparam>
    public static Result<T> Success<T>(T data)
    {
        var result = new Result<T> { Success = true, Data = data };

        return result;
    }

    /// <summary>
    /// Returns a failed result
    /// </summary>
    public static Result Failed(string errorMessage = null, Exception exception = null)
    {
        // ... unrelevant code for the question

        return new Result { Success = false, Message = errorMessage };
    }
}

如您所见,上面的代码有两种成功的返回类型,一种是如果您不想返回任何内容,另一种是如果您想要返回一些内容,但是失败的结果将永远不会返回任何内容。不必要,只有一条错误消息。 这使我想到以下几点:当我想制作一个可以使用返回类型返回成功并在没有返回类型的情况下失败的方法时,如何转换它以确定返回类型是Success(generic)还是Failed ? 在这里喜欢这种方法:

// The return type of the method is Result, because Result<T> derives from it
public static Result GetClipboardFromDateTime(DateTime dateTime)
{
    // Search for items
    IEnumerable<ClipboardCopy> items = null;
    try
    {
        items = mClipboardCollection.Where(a => a.CopiedTime == dateTime);
    }
    catch (Exception ex)
    {
        // This will return the class "Result"
        return ResultManager.Failed(
            "Something went wrong getting copies from the clipboard.", ex);
    }

    // If there is not values
    if (items.Count() <= 0)
        return ResultManager.Failed("There are not copies with that datetime.");

    // Return values
    // This will return the class Result<T>
    return ResultManager.Success(items.ToArray());
}

实际上,我正在这样做:

var clipboard2 = (Result<ClipboardCopy[]>)AClipboard.GetClipboardFromDateTime(DateTime.Today);

如果返回类型为Result(generic),但返回类型,它将崩溃,但会发生异常:

System.InvalidCastException:'无法将类型'AdvancedClipboard.Desktop.Models.Result'的对象转换为类型'AdvancedClipboard.Desktop.Models.Result'1 [AdvancedClipboard.Desktop.Models.ClipboardCopy []]'。

2 个答案:

答案 0 :(得分:3)

有一个问题,您正在尝试先转换。方法返回两种类型的结果。

  1. 成功
  2. 失败

我认为您应该这样做。

var result = AClipboard.GetClipboardFromDateTime(DateTime.Today);

if(Result.Success)
{
   var dataResult = (Result<ClipboardCopy[]>) result;
}

如果您不想进行投射。还有另一种方法,但我不建议。

var result = AClipboard.GetClipboardFromDateTime(DateTime.Today);

if(result.Success)
{
    dynamic d = result.Data;
}

在上面的示例动态中,您不需要进行强制转换,但是同时您松开了对数据的强类型访问。主要在进行互操作时建议使用。如果您可以松开强类型访问权限,则可以使用它。还有一件事,与此相关的任何错误都将在运行时执行。

答案 1 :(得分:1)

您正在尝试创建类似于Option / Maybe monad的东西:) 您可以考虑以下几种选择:

  1. 将所有内容放入“父级”

    class Result<TValue>
    {
      TValue Value // throws if !HasValue
      bool HasValue 
      string ErrorMessage // throws if HasValue
    }
    

您可以使用适当的构造函数来处理派生类型Some,None(无(成功,失败)),或者在父类型上使用静态“ create”方法+私有构造函数,或者使用扩展方法-无论您希望创建“ Success”如何, “失败”了。返回此结果的函数将始终需要指定通用参数。

  1. 您所拥有的+“ pattern match” =“类型转换” =在呼叫方投放 即

    var result = GetClipboardFromDateTime();
    switch(result)
    {
      case Success<ClipboardCopy[]>: ... break;
      case Failure: ... break;
      default: throw
    }
    

(C#7功能),或者

    if (result is Success<ClipboardCopy[]>)
    (result as Success<ClipboardCopy[]>)?.Value 

  1. 在结果类上放置成功/错误继续/回调

    Action<TValue> / Func<TValue, ?> onSuccess
    Action<string> / Func<string, ?> onError
    

    result.OnSuccess(value => /* do smthn /w value */);
    result.OnError(errorMsg => /* do smthn /w error msg */);
    

    (编辑)结果提示:

    public void OnSuccess(Action<TValue> onSuccess) {
      if (!_hasValue) return;
      onSuccess(_value);
    }
    
    public void OnError(Action<string> onError) {
      if (_hasValue) return;
      onError(_errorMessage);
    }
    
    // alternatively
    public void Apply(Action<TValue> onSuccess, Action<string> onError) {
      if (_hasValue) {
        onSuccess(_value);
        return;
      }
      onError(_errorMessage);
    }
    

    父结果将需要具有这些方法。可以通过多种方式来完成(父impl可以是“不执行任何操作-成功与失败子项将覆盖而不检查_hasValue等)”。 OnSuccess(),OnError(),Apply()不必为空,您可以使用Result<TResult> OnSuccess<TResult>(Func<TValue, TResult> onSuccess)(而不是void + Action<>)来链接它们(我在C#中不会这样:D)。

通常,在C#中,我会避​​免以这种方式从方法中“返回异常” /“错误代码”等-我相信您应该实现“快乐之路”并通过异常(如果可能)进行异常处理。我真的很喜欢在TryGet()方法上使用这种“模式”。