在C#中给定泛型类型T
,我想知道如何获取类型Q
,对于不可为空的T?
等于T
,并且{{1已经可以为空的T
。
问题来自真实的代码。我想统一访问我的ASP.NET应用程序中通过查询字符串传递的参数。我想指定相同类型的默认值,但确保T
可以作为默认值传递。
null
目前我被迫有两个public static T FetchValue<T>(
string name,
<T? for non-nullable, T otherwise> default_value = null) // How to write this?
{
var page = HttpContext.Current.Handler as Page;
string str = page.Request.QueryString[name];
if (str == null)
{
if (default_value == null)
{
throw new HttpRequestValidationException("A " + name + " must be specified.");
}
else
{
return default_value;
}
}
return (T)Convert.ChangeType(str, typeof(T));
}
重载 - 一个没有默认值,一个有它:
FetchValue
它工作正常,但我想知道是否可以合并这两个函数。
在C ++中,我会使用类型特征,例如public static T FetchValue<T>(string name);
public static T FetchValue<T>(string name, T default_value);
,对于可空和非可空类型都有两个PromoteNullable<T>::type
特化。但是C#呢?
答案 0 :(得分:3)
不直接回答所提出的问题,但我会写下:
public static T FetchValue<T>(string name)
{
T value;
if (TryFetchValue(name, out value))
return value;
throw new HttpRequestValidationException("A " + name + " must be specified.");
}
public static T FetchValue<T>(string name, T default_value)
{
T value;
if (TryFetchValue(name, out value))
return value;
return default_value;
}
private static bool TryFetchValue<T>(
string name,
out T value)
{
var page = HttpContext.Current.Handler as Page;
string str = page.Request.QueryString[name];
if (str == null)
{
value = default(T);
return false;
}
value = (T)Convert.ChangeType(str, typeof(T));
return true;
}
因此,大部分代码只存在一次 - 如果它选择的话,您现在甚至可以让调用代码选择将null
作为默认值。
即使你可以创建你想要的参数声明,这一行仍然是一个问题:
return default_value;
如果结果default_value
是T?
而不是T
,则上述代码无效。即使你做演员:
return (T)default_value;
还有一个问题 - 要从T?
转换为T
,编译器实际上必须插入一个调用来获取可空的Value
属性。但如果default_value
的类型只是T
,则该调用无效。
在C#Generics中,编译器必须为该方法创建一个IL。无法插入可能访问Value
的可选代码。
答案 1 :(得分:1)
由于您的返回类型为T
,而T
属于值类型,因此不能为空。
所以你总是要传递一个可以为空的类型,因为你想要一个null,对吗?
试试这个,它允许你传递一个可以为空的值类型(i
)和一个普通的引用类型(o
):
static void Main(string[] args)
{
int? i = 5;
object x = new object();
object o = FetchValue("x", i);
o = FetchValue("x", x);
}
private static T? FetchValue<T>(string name, T? p) where T : struct
{
T? result = (T?)FetchValue(name, (object)p);
return result;
}
private static T FetchValue<T>(string name,
T default_value = default (T)) // default(T) where T is a reference type will always be null!
where T : class
{
// do whatever you want
var page = HttpContext.Current.Handler as Page;
string str = page.Request.QueryString[name];
if (str == null)
{
if (default_value == null)
{
throw new HttpRequestValidationException("A " + name + " must be specified.");
}
else
{
return default_value;
}
}
return (T)Convert.ChangeType(str, typeof(T));
}
请记住,所有语法都是foo,因为无论如何你最终都会得到object
类型的句子。但这是必需的,因为只有object
才能为空。
答案 2 :(得分:1)
有我的例子:
//Page extension
static class PageExtensions
{
private static T FetchValue<T>(this Page page, string name, object defaultValue)
{
string str = page.Request.QueryString[name];
if (string.IsNullOrEmpty(str))
{
if (defaultValue != null)
return (T)defaultValue;
throw new HttpRequestValidationException("A " + name + " must be specified.");
}
//not the best way
return (T)Convert.ChangeType(str, typeof(T));
}
public static T FetchValueFromCurrentPage<T>(string name, T defaultValue)
{
var page = HttpContext.Current.Handler as Page;
if(page == null)
throw new InvalidOperationException("Current handler is not Page");
return page.FetchValue<T>(name, defaultValue);
}
public static T FetchValueFromCurrentPage<T>(string name) where T : class
{
return FetchValueFromCurrentPage(name, (T)null);
}
}
答案 3 :(得分:0)
尝试以下方法:
private static T FetchValue<T>(string name, Nullable<T> default_value = null) where T : struct
{
var page = HttpContext.Current.Handler as Page;
string str = page.Request.QueryString[name];
if (str == null)
{
if (default_value == null)
{
throw new Exception("A " + name + " must be specified.");
}
else
{
return (T)Convert.ChangeType(default_value, typeof(T));
}
}
return default(T);
}
Nullable类型必须是值类型,因此您必须将约束设置为struct
。您将类型T
声明为Nullable
并在有值的情况下转换类型,否则返回类型为default
的{{1}},在这种情况下为空。调用此方法时,还必须明确设置类型:
T
返回字符串
由于int? val = FetchValue<int>("name");
是可以为空的引用类型,因此您可以正确地说它不能与此函数一起使用。在这种情况下,我会创建一个新的string
,而字符串则使用它。
struct
像这样调用这个函数。
struct NullableString
{
public string Value;
}
答案 4 :(得分:0)
您可以指定 class 约束以确保调用者传入引用类型(可以为空):
public static T FetchValue<T>(
string name,
T default_value = null) where T : class //!
{
var page = HttpContext.Current.Handler as Page;
string str = page.Request.QueryString[name];
if (str == null)
{
if (default_value == null)
{
throw new HttpRequestValidationException("A " + name + " must be specified.");
}
else
{
return default_value;
}
}
return (T)Convert.ChangeType(str, typeof(T));
}
在这种情况下,您将铸造T的责任委托给T?当有必要给来电者时,无论如何这很好:他可能知道的更好!
答案 5 :(得分:0)
你可以用这种简单的方式做你想做的事:
public static T FetchValue<T>(string name, T defaultValue = null) where T : class { }
public static T? FetchValue<T>(string name, T? defaultValue = null) where T : struct { }