【问题标题】:Generic Method with optional generic parameter带有可选泛型参数的泛型方法
【发布时间】:2013-08-02 20:06:20
【问题描述】:

如何创建具有可选泛型类型的泛型方法?这就是我现在所拥有的,它有效

 public GridViewColumn<T> Column<D>(String HeaderText, D decorator) where D: IColumnDecorator, new()
        {
            GridViewColumn<T> column = new GridViewColumn<T>();
            column.HeaderText = HeaderText;
            column.Decorator = new D();
            return column;
        }

如您所见,我需要在 Column() 方法中实例化类型 D(实现 IColumnDecorator)。

问题是,“D”类型是可选的。如果为 Null,我想明确使用我拥有的默认 ColumnDecorator。像

 public GridViewColumn<T> Column<D>(String HeaderText, D decorator) where D: IColumnDecorator, new()
        {
            GridViewColumn<T> column = new GridViewColumn<T>();
            column.HeaderText = HeaderText;
            if(decorator ==null)
            {
               column.Decorator = new DefaultColumnDecorator();
            }
            else{
               column.Decorator = new D();
            }
            return column;
        }

请帮忙。谢谢!

[编辑]。

如果我有一个自定义的 IColumnDecorator 实现,我想在 razor MVC 中使用它

@Model.[IEnumerable].Grid(grid=>{
      ..
      ...
      grid.columns(
         grid.Column<MyOwnColumnDecorator>("FirstColumn")
      )
});

如果我没有任何并想使用默认值,那么我希望能够做类似的事情

@Model.[IEnumerable].Grid(grid=>{
          ..
          ...
          grid.columns(
             grid.Column("FirstColumn",null) or simply grid.Column("FirstColumn"); 
          )
    });

【问题讨论】:

  • 为什么你的第一个方法让调用者传入D 的实例?你从不使用它。
  • 为什么需要在Column()中实例化类型D?让调用者传入实例会更加灵活。
  • 贾斯汀,你是什么意思?还有玉修,我这样做是因为我不希望我的调用者在调用时必须指定具体类型的“新”关键字。
  • 如果你想要一个默认的 ColumnDecorator,只需进行重载。有一个非泛型重载 - 它使用你的 columndecorator。为泛型提供(非空)默认值是没有意义的……这更符合接口参数。
  • 你不能这样使用它。您在此调用中硬编码装饰器的类型,当您尝试实例化 DefaultDecorator 时,您的代码将无法编译,因为您的方法需要 MyOwnColumnDecorator 类型。我不知道我是否解释过自己。

标签: c# generics


【解决方案1】:

在您当前的代码中,您不需要decorator 参数,因为您创建了一个D 的新实例并使用它来代替。

public GridViewColumn<T> Column<D>(String HeaderText) where D: IColumnDecorator, new()
{
    GridViewColumn<T> column = new GridViewColumn<T>();
    column.HeaderText = HeaderText;
    column.Decorator = new D();
    return column;
}

如果你有一个默认的参数类型要使用,你不需要使用泛型:

public GridViewColumn<DefaultColumnDecorator> Column(String headerText)
{
    return Column<DefaultColumnDecorator>(headerText);
}

或者,您可以保留参数并删除 new() 约束:

public GridViewColumn<T> Column<D>(String HeaderText, D decorator) where D : IColumnDecorator
{
    GridViewColumn<T> column = new GridViewColumn<T>();
    column.HeaderText = HeaderText;
    column.Decorator = decorator;
    return column;
}

public GridViewColumn<DefaultColumnDecorator> Column(String headerText)
{
    return Column(headerText, new DefaultColumnDecorator());
}

【讨论】:

  • new D() 最好写成 default(D),我喜欢你提出的解决方案的其余部分。
  • @phillip - new D()default(D) 完全不同 - 如果default(D) 是类类型,则default(D) 为空,如果是结构,则为“零”。
  • 在没有 new() 约束的第三个代码块中,该方法根本不需要是通用的。
  • 谢谢李。在尝试了所有解决方案之后,看起来这是处理它的最佳方法。只需重载列方法。如果您有实现,请使用它。否则,使用默认值。非常感谢!
【解决方案2】:

使用默认参数:

public GridViewColumn<T> Column<D,T>(string HeaderText, D decorator = null)
    where D : IColumnDecorator, class, new()

【讨论】:

  • 谢谢ShadowCat7,现在,我该如何调用该方法?只需提供一个 null 作为第二个参数?
  • 默认参数允许您不必添加第二个参数。您可以通过仅提供字符串和类型参数来调用它。
【解决方案3】:

您不应该从Column 方法实例化类型D。相反,您应该让调用者将其传入。

 public GridViewColumn<T> Column(String HeaderText, IColumnDecorator decorator) 
        {
            GridViewColumn<T> column = new GridViewColumn<T>();
            column.HeaderText = HeaderText;
            if(decorator ==null)
            {
               column.Decorator = new DefaultColumnDecorator();
            }
            else{
               column.Decorator = decorator;
            }
            return column;
        }

理由在这里:http://en.wikipedia.org/wiki/Dependency_injection

【讨论】:

  • 使用 null 合并运算符并使装饰器可选,但这是一个好方法。
【解决方案4】:

在使用了一些选项后,我制作了一个适用于以下代码的示例:

public GridViewColumn<TResult> Column<TColumn>(string HeaderText, TColumn decorator = null) where TColumn : class, IColumnDecorator
{
    GridViewColumn<TResult> column = new GridViewColumn<TResult>();
    column.HeaderText = HeaderText;

     if(decorator == null)
     {
        column.Decorator = new DefaultColumnDecorator();
     }
     else
     {
        column.Decorator = decorator;  
     }     

    return column;
}

为了完成这项工作,您需要考虑以下几点:

  • 您需要添加类限制才能将您的参数定义为空。
  • 您不应使用 default(TColumn),因为由于类限制,它会返回 null。
  • 由于某种原因,如果您使用“? :”语法,编译器不接受您实例化 DefaultColumnDecorator 类型,但如果您使用常规 if 语句,它可以工作。
  • 似乎不需要 new() 限制才能使其正常工作。

在我的示例中,我可以通过以下两种方式调用此方法,结果相同:

@Model.[IEnumerable].Grid(grid=>{
      ..
      ...
      grid.columns(
         grid.Column<MyOwnColumnDecorator>("FirstColumn",null);
      )
});

@Model.[IEnumerable].Grid(grid=>{
      ..
      ...
      grid.columns(
         grid.Column<MyOwnColumnDecorator>("FirstColumn");
      )
});

祝你好运!

【讨论】:

  • 哇,谢谢塞尔吉奥!这与我现在拥有的代码几乎相同。我忘记提到的问题是,在上面的代码中,“MyOwnColumnDecorator”应该是可选的,并且只有在您有一个实现 IColumnDecorator 的自定义类时才需要。抱歉,我不擅长提问。
【解决方案5】:

有很多方法可以做你想做的事,让调用者传递一个方法来做一个装饰器怎么样?

public GridViewColumn<T> Column(string HeaderText, Func<IColumnDecorator> decoratorGenerator)
{
  GridViewColumn<T> column = new GridViewColumn<T>();
  column.HeaderText = HeaderText;
  column.Decorator = decoratorGenerator != null ? decoratorGenerator()
    : new DefaultColumnDecorator() ;
  return column;
}

【讨论】:

  • 谢谢大卫。我不想要这种方法的原因是因为我不想让最终用户更难使用它,所以在所有测试/跟踪之后,我更喜欢 Lee 的方法。不过还是谢谢。非常感谢。
【解决方案6】:

我看到你缺少两件事。一种是默认参数设置decorator = null,另一种是使用default(T)。我已将您的改写如下,但我显然无法对其进行测试 - 不过应该很接近。

public GridViewColumn<TResult> Column<TColumn>(string HeaderText, TColumn decorator = null) where TColumn : IColumnDecorator, new()
{
    GridViewColumn<TResult> column = new GridViewColumn<TResult>();
    column.HeaderText = HeaderText;
    column.Decorator = decorator == null ? new DefaultColumnDecorator() : default(TColumn);

    return column;
}

【讨论】:

  • philip,如果我希望装饰器是可选的或为空,我该如何调用此代码?我试图指定 ...Column("Header Text", null),它说尝试明确指定用法。
  • Column("HeaderText") 是你所要做的,因为第二个参数已经指定了。
  • 如果调用者提供了decorator,您应该将Decorator 赋值替换为column.Decorator = decorator ?? new DefaultColumnDecorator();,因为您当前正在分配null(如果D 是类类型)。
  • Key Phillip,我试过这样做... grid.column("HeaderText"),但出现错误。但如果我确实提供了一个具体的类型,那么我就像 grid.Column("FirstColumn") 一样好。顺便说一句,我必须在约束中添加一个“类”,否则,它不会让我设置为 null
  • 使用 default(TColumn) 只会给你 null,但这根本不是你想要的。您应该使用空合并运算符: column.Decorator = decorator ?? new DefaultColumnDecorator() 或坚持 Lee 的解决方案。如果你不做 new D(),你的第二个参数可以只是 IColumnDecorator 并且它根本不需要是通用的。
猜你喜欢
  • 1970-01-01
  • 2016-03-31
  • 2017-10-06
  • 1970-01-01
  • 2021-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多