处理泛型和静态类型之间的交互

时间:2013-05-30 05:12:55

标签: c# generics

我正在编写一个通用方法,我希望根据泛型Type参数的类型生成不同的执行路径。不同的执行路径是静态类型的,例如

public static T Get<T>(this NameValueCollection collection, string name) where T : struct
{
    //Perform test on type, if it matches, delegate to statically typed method.
    if (typeof(T) == typeof(int)) return (T)(object)GetInt32(collection, name);
    else if (typeof(T) == typeof(DateTime)) return (T) (object) GetDateTime(collection, name);

    //Other types parsed here...

    //If nothing matched, return default.
    return default(T);
}

我发现能够使用静态执行路径的返回结果的唯一方法是将其作为对象包装,然后将其强制转换为“T”。

对我而言,这首先取消了使用通用方法的目的(除了获得一些语法糖)。在我们已经确定T是int类型的情况下,是否有人知道能够将int值返回为T的方法?

我正在考虑使用'动态'类型的var,但是读到它只是在幕后使用对象结束。

这可能是仿制药的范围吗?

-

更新以包含我的最终方法,基于smartcaveman的回复,它使用通用静态类的类型解析来确定使用哪种“解析”方法,而无需装箱或使用动态。

public class StringParser
{
    private class Getter<T>
    {
        private static readonly ConcurrentDictionary<StringParser, Getter<T>> Getters = new ConcurrentDictionary<StringParser, Getter<T>>();

        public Func<string, T> Get { get; set; }

        private Getter() {}

        public static Getter<T> For(StringParser stringParser)
        {
            return Getters.GetOrAdd(stringParser, x => new Getter<T>());
        }
    }

    public virtual T Get<T>(string value)
    {
        var get = Getter<T>.For(this).Get;

        if (get == null) throw new InvalidOperationException(string.Format("No 'get' has been configured for values of type '{0}'.", typeof (T).Name));

        return get(value);
    }

    public void SetupGet<T>(Func<string, T> get)
    {
        Getter<T>.For(this).Get = get;
    }
}

使用起来相当简单:

public static void Usage()
{
    StringParser parser = new StringParser();
    parser.SetupGet(Int32.Parse);            
    int myInt = parser.Get<int>("3");            
}

smartcaveman方法的技巧是具有不同类型参数的通用静态类实际上被认为是不同的类型,并且不共享静态成员。

很酷的东西,谢谢!

4 个答案:

答案 0 :(得分:2)

除非您可以使用强类型的键控集合(例如Dictionary<string,int>),否则您将不得不将该值设置为限定值。没有解决方法。

话虽如此,目前还不清楚你的方法如何比非通用版本更有用。我没有看到调用代码,但似乎唯一相关的情况是int,因为每个其他情况只返回默认值。

另外,你对dynamic

是正确的

更新

如果没有多种可能类型的值,则使用字典的想法将适用。但是,如果只有一种可能的值类型(例如int),那么您可以使用Dictionary<string,int>而不是NameValueCollection

我写了一个小样本,可能会为您提供有关如何使用自定义类保持强类型的想法。我省略了空检查和参数验证逻辑,这既没有经过测试也没有编译过。但是,基本的想法应该是清楚的。您可以将ValueRegistry课程与NameValueCollection一起使用,如下所示。

 //  around application start, configure the way values are retrieved   
 //  from names for different types.  Note that this doesn't need to use
 //  a NameValueCollection, but I did so to stay consistent.

 var collection = new NameValueCollection();
 ValueRegistry.Configure<int>(name => GetInt32(collection,name));
 ValueRegistry.Configure<DateTime>(name => GetDateTime(collection,name));

 // where you are going to need to get values
 var values = new ValueRegistry();      
 int value = values.Get<int>("the name"); // nothing is boxed

public class ValueRegistry
{
       private class Provider<T> 
            where T : struct
       {
             private static readonly ConcurrentDictionary<ValueRegistry,Provider<T>> Providers = new ConcurrentDictionary<ValueRegistry,Provider<T>>();
              public static Provider<T> For(ValueRegistry registry)
              {
                  return Providers.GetOrAdd(registry, x => new Provider<T>());
              }
              private Provider(){
                 this.entries = new Dictionary<string,T>();
              }
              private readonly Dictionary<string,T> entries;
              private static Func<string,T> CustomGetter;
              public static void Configure(Func<string,T> getter) { CustomGetter = getter;}

              public static T GetValueOrDefault(string name)
              {
                   T value;
                    if(!entries.TryGetValue(name, out value))
                       entries[name] = value = CustomGetter != null ? CustomGetter(name) : default(T);
                     return value;
              }
       }

       public T Get<T>(string name) 
          where T : struct
       {
           return Provider<T>.For(this).GetValueOrDefault(name);
       }

       public static void Configure<T>(Func<string,T> customGetter)
                  where T : struct
       {
          Provider<T>.Configure(customGetter);      
       }

}

答案 1 :(得分:2)

使用C#4.0中引入的动态功能可以实现这种调度(下一版本也支持它)。

此代码执行您在此处表达的内容:

public static T Get<T>(this NameValueCollection collection, string name) where T : struct
{
    T v = default(T);
    dynamic indicator = v;

    return GetValue(collection, name, indicator);
}

static int GetValue(NameValueCollection collection, string name, int indicator)
{
    return 110;
}

static DateTime GetValue(NameValueCollection collection, string name, DateTime indicator)
{
    return DateTime.Now;
}

// ... other helper parsers

// if nothing else matched
static object GetValue(NameValueCollection collection, string name, object indicator)
{
    return indicator;
}

进行烟雾测试:

Console.WriteLine(Get<int>(null, null));
Console.WriteLine(Get<DateTime>(null, null));
Console.WriteLine(Get<double>(null, null));

答案 2 :(得分:1)

是的,你必须明确表示(将值类型转换为对象),然后再将其转换为通用T,即使你已经声明where T : struct。你可以做类似下面的事情,但我不能说它更优雅。

return (T) Convert.ChangeType(GetInt32(collection, name), typeof (int));

答案 3 :(得分:0)

您可以使用Dictionary来处理此问题。

private static Dictionary<Type, Func<NameValueCollection, string, T>> _typeMap = new Dictionary<Type, Func<NameValueCollection, string, T>>();

static Constructor()
{
    _typeMap[typeof(DateTime)] = (nvc, name) => { return (T)GetDateTime(nvc, name); };
    // etc
}


public static T Get<T>(this NameValueCollection collection, string name) where T : struct
{
    return _typeMap[typeof(T)](collection, name);
}