【问题标题】:How to specify in C# generics such T that is constructible from string? (generic type constraint)如何在 C# 泛型中指定可从字符串构造的 T? (通用类型约束)
【发布时间】:2015-03-20 15:31:20
【问题描述】:

我想为我的 T 指定所需的默认构造选项:

  public interface IParameter<T>  /* where T : T(string) */ { 
     T Value { get; set; }
  }

所以我可以从给定的字符串构造它,如果可以这样传递:

Value  = "bla";

或者至少是这样的:

Value = new T("bla");

那么如何在 C# 泛型中指定可以从字符串构造的 T 呢?

【问题讨论】:

  • string 不能被子类化,那么首先使它成为通用的有什么意义呢?
  • @JeroenVannevel:这不是关于子类化,而是关于任意类型 T 具有接受 string 参数的构造函数。
  • 任何 T 都有 ToString(地址、时间、网址等),许多此类类型都可以从字符串构造。
  • 啊,所以它是关于一个将字符串作为参数的构造函数?因为这与第一次展示的用法完全不同。
  • 这似乎是XY Problem

标签: c# .net generics constraints .net-4.5


【解决方案1】:

不幸的是,C# 没有为泛型参数提供任意构造函数签名限制。只有a restricted number of constraints are supported,其中最接近的是new constraint。但是,它仅用于强制执行无参数构造函数。

但是,您可以通过使用带有string 并返回T 的工厂对象来解决这个缺点。首先,为此类工厂对象定义一个接口:

public interface IFactory<T>
{
    T Create(string str);
}

随后,您可以在界面中使用该工厂类型:

public interface IParameter<TFactory, T>
    where TFactory : IFactory<T>
{ 
    T Value { get; set; }
}

如果你希望能够随意实例化工厂,你可以要求它们有一个无参数的构造函数:

public interface IParameter<TFactory, T>
    where TFactory : new(), IFactory<T>
{ 
    T Value { get; set; }
}

然后,您可以使用通用方法基于string 实例化T,例如作为界面的扩展方法:

public static class ParameterUtilities
{
    public static void AssignValue<TFactory, T>(this IParameter<TFactory, T> prm, string str)
        where TFactory : new(), IFactory<T>
    {
        var factory = new TFactory();
        prm.Value = factory.Create(str);
    }
}

作为一个如何使用它的例子,让我们假设变量myPrm 是一个实例,它使用适当的类型参数实现你的IParameter 接口。然后你可以调用这样的东西:

myPrm.AssignValue("Hello, World!");

【讨论】:

    【解决方案2】:

    不幸的是,这种限制是不合法的。只允许无参数构造函数约束:

    where T : new()
    

    【讨论】:

      【解决方案3】:

      你不能,因为在泛型类型约束中你不能说该类型必须有一个特定的构造函数(只是它必须有一个无参数的构造函数),也不能说它必须有特定的方法/操作符。

      但是

      public interface IFromString<T>
      {
          void DeserializeFromString(string str);
      }
      
      public class MyType : IFromString<MyType>
      {
          public int Value;
      
          public void DeserializeFromString(string str)
          {
              Value = int.Parse(str);
          }
      }
      
      public interface IParameter<T> where T : IFromString<T>, new()
      {
          T Value { get; set; }
      }
      
      public class Parameter<T> : IParameter<T> where T : IFromString<T>, new()
      {
          T Value { get; set; }
      
          public void Load(string str)
          {
              Value = new T();
              Value.DeserializeFromString(str);
          }
      }
      

      一个经典的例子...一个接口说可以从(某物)反序列化一个类型(xml 经常:-))

      用途:

      Parameter<MyType> parameter = new Parameter<MyType>();
      parameter.Load(someStringLoadedFromSomewhere);
      

      【讨论】:

      • 在某些方面稍微好一点的方法可能是指定任何类型都不能合法地实现IFromString&lt;out T&gt;,除非它或父类型包含静态工厂方法T ConstructFromString(String st),并且有一个静态泛型类可以使用缓存委托为任何特定类型U:IFromString&lt;T&gt; 调用此类方法。第一次访问任何类型U 时需要反射查找,但之后是简单的委托调度。这将避免需要 new 约束。
      【解决方案4】:

      您不能在接口中指定构造函数。根据您的要求,您可以使用抽象类:

      VS 机器上的伪代码:

      public abstract class BaseParameter<T>
      { 
          public abstract void BaseParameter(string something);
      
          T Value { get; set; }
      }
      

      【讨论】:

      • 这如何回答这个问题? OP 不是询问接口的构造函数,而是要求来自T 的特定构造函数。
      • 你不能有一个带有类名的方法,也不能告诉你需要一个构造函数以这种方式实现(我不确定BaseParameter(string something)是一个方法还是构造函数)
      • @O.R.Mapper 建议OP可能希望根据实际需求查看抽象类而不是接口。它们在不同的时间有用。
      • @xanatos - 构造函数。正如我所说,不是在带有 VS 的 PC 上,所以不能证明 100%,但想展示抽象类,因为很多人在使用接口等时会忽略。
      • @Belogix 没有抽象构造函数之类的东西。您定义了一个与类同名的抽象方法,这也是不允许的。
      猜你喜欢
      • 2017-01-21
      • 2023-03-17
      • 2011-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多