【问题标题】:Overloading a method which accepts `object` as default parameter type重载接受`object`作为默认参数类型的方法
【发布时间】:2012-12-19 22:49:38
【问题描述】:

我需要能够调用一个方法并传入一个未知类型的对象 但随后调用正确的重载。我还需要一个接受的默认实现 object 作为其参数类型。我看到的是默认重载是唯一被使用过的。

这是我想要做的事情的要点:

class Formatter
{
  private object Value;

  public Formatter(object val){
    Value = val;
  }

  public override string ToString()
  {
    return Format(Value);
  }

  private string Format(object value)
  {
    return value.ToString();
  }

  private string Format(DateTime value)
  {
    return value.ToString("yyyyMMdd");
  }
}

好的,到目前为止一切顺利。现在我希望能够做到这一点:

public static class FancyStringBuilder()
{
  public static string BuildTheString()
  {
    var stringFormatter = new Formatter("hello world");
    var dateFormatter = new Formatter(DateTime.Now);
    return String.Format("{0} {1}", stringFormatter, dateFormatter);
  }
}

FancyStringBuilder.BuildTheString() 的结果是"hello world 2012-12-21 00:00:00.000",而我期望的是"hello world 20121221"

问题是没有调用接受DateTime 的重载,而是默认为接受object 的重载。 如何在不使用混乱的 switch 语句的情况下调用正确的方法?

【问题讨论】:

标签: c#-4.0 overloading


【解决方案1】:

Formatter.ToString() 中,覆盖Formatter.Format(object)始终调用。这是因为重载决议发生在编译时,而不是运行时。在编译时,关于 Value 的唯一已知就是它是一个对象。

如果你真的想区分传入的类型,你需要在Formatter的构造函数中这样做。在这种情况下,您可以立即调用ToString() 并只存储格式化结果,而不是挂在对象上:

class Formatter
{
    string formattedValue;

    public Formatter(object value)
    {
        formattedValue = value.ToString();
    }

    public Formatter(DateTime value)
    {
        formattedValue = value.ToString("yyyyMMdd");
    }

    public string ToString()
    {
        return formattedValue;
    }
}

请注意,这确实假设您的对象在您创建 Formatter 对象和调用 Formatter.ToString() 之间没有变化,或者至少可以拍摄字符串表示的快照在创建 Formatter 时。

这还假设您在编译时就知道传入的类型。如果您想要一个真正仅运行时的解决方案,则必须使用“is”运算符或typeof() 比较。

如果您的目标只是提供基于传入类型的自定义 ToString() 格式,我可能会使用从类型映射到格式字符串的列表:

static class Formatter
{
    private static List<Tuple<Type, string>> Formats;

    static Formatter()
    {
        Formats = new List<Tuple<Type, string>>();

        // Add formats from most-specific to least-specific type.
        // The format string from the first type found that matches
        // the incoming object (see Format()) will be used.
        AddMapping(typeof(DateTime), "yyyyMMdd");
        // AddMapping(typeof(...), "...");
    }

    private static void AddMapping(Type type, string format)
    {
        Formats.Add(new Tuple<Type, string>(type, format));
    }

    public static string Format(object value)
    {
        foreach (var t in Formats)
        {
            // If we find a type that 'value' can be assigned to
            // (either the same type, a base type, or an interface),
            // consider it a match, and use the format string.
            if (t.Item1.IsAssignableFrom(value.GetType()))
            {
                return string.Format(t.Item2, value);
            }
        }

        // If we didn't find anything, use the default ToString()...
        return value.ToString();
    }
}

这样,调用代码如下所示:

Console.WriteLine(
    "{0} {1}",
    Formatter.Format(DateTime.Now),
    Formatter.Format("banana"));

【讨论】:

    【解决方案2】:

    我认为这是因为类构造函数将object 作为参数,然后将该对象分配给变量Value,该变量也是object。因为Valueobject 类型,所以调用 Format(object)

    试试这个

    public override string ToString()
    {
        if(Value is DateTime)
          return Format(Convert.ToDateTime(Value)); //this should call the right method
    
        return Format(Value); //works for other non-custom-format types e.g. String
    }
    

    【讨论】:

    • 这个例子不太行。有可能出现两打左右的超载。
    • 我认为您应该只担心具有自定义格式的那些 - 从您的问题来看,它只是日期。
    • 我最终选择了这种类型的解决方案。我尝试使用每个格式化程序的子类来实现,但它对代码库的其余部分造成了很大的破坏。这是蔓延最少的最简单的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 2018-03-25
    • 1970-01-01
    • 2016-11-14
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多