简体   繁体   English

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

[英]Handling interactions between generic and static types

I am writing a generic method where I would like a different execution path to occur based on the type of the generic Type parameter. 我正在编写一个通用方法,其中我希望基于通用Type参数的类型发生不同的执行路径。 The different execution paths are statically typed, eg 不同的执行路径是静态类型的,例如

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);
}

The only way I've found to be able to use the return result of the static execution path is to box it as an object, and then cast it to 'T'. 我发现能够使用静态执行路径的返回结果的唯一方法是将其装箱为对象,然后将其强制转换为“ T”。

To me this defeats the purpose of using a generic method in the first place (other than gaining some syntactical sugar). 对我而言,这首先挫败了使用通用方法的目的(除了获得一些语法上的糖以外)。 Does anyone know a way of being able to return the int value as T, in cases where we have already established that T is of type int? 在已经确定T为int类型的情况下,有人知道将int值返回为T的方法吗?

I was looking at using a var of type 'dynamic' but read that it just end sup using object behind the scenes anyway. 我当时在看使用'dynamic'类型的var,但是我读到它只是在幕后使用对象结束。

Is this, perhaps, the extent of generics? 这可能是泛型的范围吗?

-- -

Updated to include my final approach, based on smartcaveman's reply, which uses the type resolution of generic static classes to determine which 'parsing' method to use, without boxing or using dynamic. 根据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;
    }
}

Fairly simple to use: 使用起来相当简单:

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

The trick of smartcaveman's method is that generic static classes with different type parameters are actually considered different Types, and do not share static members. smartcaveman方法的诀窍是具有不同类型参数的泛型静态类实际上被视为不同类型,并且不共享静态成员。

Very cool stuff, thank you! 很酷的东西,谢谢!

Unless you can use a strongly typed keyed collection (eg Dictionary<string,int> ), you're going to have to box the value. 除非您可以使用强类型键控集合(例如Dictionary<string,int> ),否则必须将值装箱。 There is not a work around. 没有解决方法。

That being said, it is not clear how your method is anymore useful than a non-generic version. 话虽如此,还不清楚您的方法是否比非通用版本有用。 I'm not seeing the calling code, but it seems like the only relevant case is the int, since every other case just returns the default value. 我没有看到调用代码,但似乎唯一相关的情况是int,因为其他所有情况都只是返回默认值。

Also, you're correct about dynamic . 另外,您对dynamic是正确的。

Update 更新资料

The idea of using a dictionary would apply if there were not multiple possible types of value. 如果没有多种可能的值类型,则使用字典的想法将适用。 However, if there were only 1 possible type of value (eg int) then you could use a Dictionary<string,int> instead of a NameValueCollection . 但是,如果只有一种可能的值类型(例如int),则可以使用Dictionary<string,int>代替NameValueCollection

I wrote a small sample that might give you ideas as to how you could keep strong typing with a custom class. 我写了一个小样本,可能会为您提供有关如何使用自定义类保持强类型输入的想法。 I have omitted null-checking and argument validation logic, and this has neither been tested nor compiled. 我已经省略了null检查和参数验证逻辑,并且这尚未经过测试或编译。 However, the basic idea should be clear. 但是,基本思路应明确。 You could use the ValueRegistry class with your NameValueCollection as follows. 您可以将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);      
       }

}

That kind of dispatch is possible using dynamic features introduced in C# 4.0 (and next versions support it too). 使用C#4.0中引入的动态功能可以进行这种分派(并且下一版本也支持它)。

This code does what you've expressed here: 这段代码可以完成您在此处表达的内容:

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;
}

To perform a smoke test: 要执行烟雾测试:

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

Yes you have to box (cast value type to object) explicitly before casting it to generic T even you have stated where T : struct . 是的,即使您已经声明where T : struct也必须在将其强制转换为泛型T之前显式地装箱(将值类型转换为对象)。 You can do something like below but I can not say it is more elegant. 您可以执行以下操作,但我不能说它更优雅。

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

You could use a Dictionary to handle this. 您可以使用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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM