我正在编写一个通用方法,我希望根据通用 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 的回复更新了我的 final方法,该方法使用通用静态类的类型解析来确定要使用的“解析”方法,无需装箱或使用动态。
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方法的技巧在于,具有不同类型参数的泛型静态类实际上被认为是不同的类型,并且不共享静态成员。
非常酷的东西,谢谢!
最佳答案
除非您可以使用强类型键控集合(例如 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);
}
}
关于c# - 处理泛型和静态类型之间的交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16828403/