我有一个班级:
class abc <T> {
private T foo;
public string a {
set {
foo = T.parse(value);
}
get{
return foo.toString();
}
}
}
然而,T.parse命令给了我一个错误。任何人都可以做我想做的事情吗?
我将它用作其他一些派生类的基类。
编辑:
我结束了我们的工作:
Delegate parse = Delegate.CreateDelegate(typeof(Func<String, T>), typeof(T).GetMethod("Parse", new[] { typeof(string) }));
我在构造函数
中执行过一次然后我在我的财产中执行以下操作:
lock (lockVariable)
{
m_result = (T)parse.DynamicInvoke(value);
dirty = true;
}
答案 0 :(得分:8)
C#泛型类型不是C ++ 模板。模板允许您进行花哨的“搜索和替换”,您将替换为T实现静态解析方法的类型的名称.C#泛型不是像这样的文本搜索和替换机制。相反,他们在类型上描述参数化多态。使用模板,所需的只是您替换参数的特定参数都是好的。使用通用的每个可能的替换无论你是否真的这样做,都必须做得好。
评论者问道:
当需要等同于Haskell的Read类型时,C#的处理方式是什么?
现在我们来讨论原始问题背后的深层问题。
为不熟悉Haskell的读者澄清:自C#2.0以来,C#支持“通用”类型,这种类型比常规类型“更高”。你可以说List<int>
并为List<T>
模式后面的一个新类型,但它是一个特定的整数列表。
Haskell在其类型系统中支持甚至更高类型。对于泛型类型,您可以说“每个MyCollection<T>
都有一个方法GetValue
,它接受一个int并返回一个T
,对于您想要命名的任何T
”。对于泛型类型,你可以在T上设置约束并说“而且,T保证实现IComparable<T>
...”使用Haskell类型类,你可以更进一步说道德等同于“...而且, T保证有一个静态方法Parse
,它接受一个字符串并返回一个T“。
“Read”类型类特别是声明道德等价物的类型类“遵循读类型类模式的类C是一个采用字符串Parse并返回C的方法”。
C#不支持那种更高级的类型。如果确实如此,那么我们可以在语言本身中检测模式,例如monad,现在只能通过将它们编入编译器来进行类型检查(例如,以查询理解的形式)。(参见{ {3}}以获得更多想法。)
由于无法在类型系统中表示该想法,因此您很难停止使用泛型来解决此问题。类型系统根本不支持这种通用性级别。
人们有时做一些可怕的事情:
static T Read<T>(string s)
{
if (typeof(T) == typeof(int)) return (T)(object)int.Parse(s);
if ...
但在我看来,这有点滥用;它确实不是泛型。
答案 1 :(得分:3)
你可以使用反射。您无法通过通用参数访问静态成员。
class Abc<T> {
private T foo;
public string a {
set {
foo = Parse<T>(value);
}
get {
return foo.ToString();
}
}
static T Parse<T>(string s)
{
var type = typeof(T);
var method = type.GetMethod("Parse", new[] { typeof(string) });
return (T)method.Invoke(null, new[] { s });
}
}
答案 2 :(得分:2)
C#没有模板。 .NET泛型不像C ++模板那样工作。
使用适当的约束,您可以对具有泛型类型的参数使用实例方法,但是无法约束静态成员。
但是,您可以使用typeof(T).GetMethod("Parse")
之类的反射来制作Func<string,T>
代表。
答案 3 :(得分:1)
您不能在泛型类上调用静态方法。 看看这篇文章:Calling a static method on a generic type parameter
但这是一个小解决方法:
public interface iExample
{
iExample Parse(string value);
}
class abc<T> where T : iExample, new()
{
private T foo;
public string a
{
set
{
foo = (T)(new T().Parse(value));
}
get
{
return foo.ToString();
}
}
}
因此,如果您有一个实现iExample
public class SelfParser : iExample
{
public iExample Parse(string value)
{
return new SelfParser();
}
}
您将能够像这样使用它:
abc<SelfParser> abcInstance = new abc<SelfParser>();
abcInstance.a = "useless text";
string unParsed = abcInstance.a; // Will return "SelfParser"
答案 4 :(得分:1)
通用参数中不知道T.parse。你必须让它知道。 不要用反射。在这种情况下,它的速度很慢,而且通常是一个糟 以正确的方式使用泛型。
您必须指定T只能是实现包含解析方法的接口的类:
class abs<T> where T : IParsable<T>
{
//your implementation here
}
interface IParsable<T>
{
T Parse(string value);
}
public class Specific : IParsable<Specific>
{
public Specific Parse(string value)
{
throw new NotImplementedException();
}
}
答案 5 :(得分:0)
虽然你不能用Generics做到这一点(没有类型约束来强制执行特定的方法签名,只有struct / object / interface约束。)
您可以创建一个基类,其构造函数采用Parse方法。请参阅底部的Int32实现。
class MyParseBase <T>
{
public MyBase (Func<string,T> parseMethod)
{
if (parseMethod == null)
throw new ArgumentNullException("parseMethod");
m_parseMethod = parseMethod;
}
private T foo;
public string a {
set
{
foo = m_parseMethod(value);
}
get
{
return foo.toString();
}
}
}
class IntParse : MyParseBase<Int32>
{
public IntParse()
: base (Int32.Parse)
{}
}
答案 6 :(得分:0)
这是Oleg G's answer的变体,无需new()
类型约束。我们的想法是为Parser
中包含的每种类型设置一个abs
,并将其注入 - 这也是Func<string, T>
方法的形式化。
interface IParser<T>
{
T Parse(string value);
}
class abs<T>
{
private readonly IParser<T> _parser;
private T foo;
public abs(IParser<T> parser)
{
_parser = parser;
}
public string a {
set
{
foo = _parser.Parse(value);
}
get
{
return foo.ToString();
}
}
答案 7 :(得分:0)
class abc<T> {
private T foo;
public string a {
set {
var x_type = typeof(T);
foo = (T)x_type.InvokeMember("Parse", System.Reflection.BindingFlags.InvokeMethod, null, value, new []{value});
}
get{
return foo.ToString();
}
}
}