将字符串与 class 相关联,以便在 class 作为泛型类型参数给出时我可以检索该字符串
associate a string with a class so that I can retrieve the string if class is given as generic type parameter
对于不同的 classes 我需要在 class 上附加一个字符串(即 Class1
有字符串 hello
,Class2
有字符串world
等)。然后我将在某个地方有一个泛型类型参数 T
,它将是(在运行时)这些 class 之一。我需要能够从该泛型类型参数中检索关联的字符串。
我该如何设置它并让它发挥作用?
因为所有 class 都是我写的,我可以使用所有可能的方法(例如为它们定义公共接口或公共基础 class 或其他)。
我尝试创建一个基础 class,它有一个包含字符串的 public 静态字段,并且对于每个实际的 class "overwrite"(隐藏基础并创建新的)字符串。但事实证明,当只有类型参数 T
.
时,我仍然无法检索字符串
public class BaseClass
{
public static string Get => "";
}
public class Class1 : BaseClass
{
public static new string Get => "hello";
}
public class Class2 : BaseClass
{
public static new string Get => "world";
}
public class Testing<T> where T : BaseClass
{
public void Test()
{
string s = T.Get;
// compiler error: "'T' is a type parameter, which is not valid in the given context"
// strangely though, BaseClass.Get and Class1.Get and Class2.Get work fine!
}
}
实际用例:
我有一个静态 class MySerializer<T>
应该反序列化 T
类型的对象。在反序列化期间,我想验证我的 T
类型的对象是否符合与 T
.
类型关联的模式
为了验证我需要先添加一个模式。对于每个可以反序列化的 class T
都有一个不同的模式,我将其作为嵌入式资源存储在我的项目中,因此每个模式都有一个路径(如文件路径)。这意味着:对于每个 class T
,我需要将一个字符串(路径)与 class 相关联,以便我能够从 T
中获取该路径。
这是我的序列化程序和模式添加过程的相关部分:
public static class MySerializer<T>
{
private static readonly XmlSerializer _mySerializer = new XmlSerializer(typeof(T));
private static readonly XmlReaderSettings _settings = new Func<XmlReaderSettings>(() =>
{
System.Reflection.Assembly assy = typeof(MySerializer<T>).Assembly;
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null,
XmlReader.Create(assy.GetManifestResourceStream(T.GetAssociatedString())));
// T.GetAssociatedString(): How to make this work?
return new XmlReaderSettings
{
Schemas = schemas,
ValidationType = ValidationType.Schema,
ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings |
XmlSchemaValidationFlags.ProcessIdentityConstraints
};
})();
public static T Deserialize(Stream strm)
{
using (XmlReader reader = XmlReader.Create(strm, _settings))
{
return (T)_mySerializer.Deserialize(reader);
}
}
}
您可以使用反射来做到这一点,但请注意所有属性都必须是静态的(您上面的示例代码将非静态属性引入派生的 class).
这是一个可编译的例子:
using System;
namespace Demo
{
public class BaseClass
{
public static string Get => "";
}
public class Class1 : BaseClass
{
public new static string Get => "hello";
}
public class Class2 : BaseClass
{
public new static string Get => "world";
}
public class Testing<T> where T : BaseClass
{
public string Test()
{
var property = typeof(T).GetProperty("Get");
if (property != null)
return (string) property.GetValue(null, null);
return null;
}
}
class Program
{
static void Main()
{
var test1 = new Testing<Class1>();
Console.WriteLine(test1.Test()); // Prints "hello"
var test2 = new Testing<Class2>();
Console.WriteLine(test2.Test()); // Prints "world"
}
}
}
在此代码中,编译和工作实际上不需要 where T : BaseClass
,但您可能希望保留它以明确它仅应与 [=21 一起使用=]es 继承自 BaseClass
.
由于静态方法和泛型类型参数在 C# 中不能一起工作(感谢 Matthew Watson 链接到 Eric Lippet 的博客)而且我不想创建 T
的新实例只是为了能够调用方法,我会用属性。
[AttributeUsage(AttributeTargets.Class)]
class SomeStringAttribute : Attribute
{
public string SomeString { get; set; }
public SomeStringAttribute(string s)
{
SomeString = s;
}
}
[SomeString("hello")]
public class Class1
{
}
[SomeString("world")]
public class Class2
{
}
public class Testing<T>
{
public void Test()
{
string s =
((SomeStringAttribute)typeof(T).GetCustomAttributes(typeof(SomeStringAttribute),
false)[0]).SomeString;
}
}
对于不同的 classes 我需要在 class 上附加一个字符串(即 Class1
有字符串 hello
,Class2
有字符串world
等)。然后我将在某个地方有一个泛型类型参数 T
,它将是(在运行时)这些 class 之一。我需要能够从该泛型类型参数中检索关联的字符串。
我该如何设置它并让它发挥作用?
因为所有 class 都是我写的,我可以使用所有可能的方法(例如为它们定义公共接口或公共基础 class 或其他)。
我尝试创建一个基础 class,它有一个包含字符串的 public 静态字段,并且对于每个实际的 class "overwrite"(隐藏基础并创建新的)字符串。但事实证明,当只有类型参数 T
.
public class BaseClass
{
public static string Get => "";
}
public class Class1 : BaseClass
{
public static new string Get => "hello";
}
public class Class2 : BaseClass
{
public static new string Get => "world";
}
public class Testing<T> where T : BaseClass
{
public void Test()
{
string s = T.Get;
// compiler error: "'T' is a type parameter, which is not valid in the given context"
// strangely though, BaseClass.Get and Class1.Get and Class2.Get work fine!
}
}
实际用例:
我有一个静态 class MySerializer<T>
应该反序列化 T
类型的对象。在反序列化期间,我想验证我的 T
类型的对象是否符合与 T
.
为了验证我需要先添加一个模式。对于每个可以反序列化的 class T
都有一个不同的模式,我将其作为嵌入式资源存储在我的项目中,因此每个模式都有一个路径(如文件路径)。这意味着:对于每个 class T
,我需要将一个字符串(路径)与 class 相关联,以便我能够从 T
中获取该路径。
这是我的序列化程序和模式添加过程的相关部分:
public static class MySerializer<T>
{
private static readonly XmlSerializer _mySerializer = new XmlSerializer(typeof(T));
private static readonly XmlReaderSettings _settings = new Func<XmlReaderSettings>(() =>
{
System.Reflection.Assembly assy = typeof(MySerializer<T>).Assembly;
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null,
XmlReader.Create(assy.GetManifestResourceStream(T.GetAssociatedString())));
// T.GetAssociatedString(): How to make this work?
return new XmlReaderSettings
{
Schemas = schemas,
ValidationType = ValidationType.Schema,
ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings |
XmlSchemaValidationFlags.ProcessIdentityConstraints
};
})();
public static T Deserialize(Stream strm)
{
using (XmlReader reader = XmlReader.Create(strm, _settings))
{
return (T)_mySerializer.Deserialize(reader);
}
}
}
您可以使用反射来做到这一点,但请注意所有属性都必须是静态的(您上面的示例代码将非静态属性引入派生的 class).
这是一个可编译的例子:
using System;
namespace Demo
{
public class BaseClass
{
public static string Get => "";
}
public class Class1 : BaseClass
{
public new static string Get => "hello";
}
public class Class2 : BaseClass
{
public new static string Get => "world";
}
public class Testing<T> where T : BaseClass
{
public string Test()
{
var property = typeof(T).GetProperty("Get");
if (property != null)
return (string) property.GetValue(null, null);
return null;
}
}
class Program
{
static void Main()
{
var test1 = new Testing<Class1>();
Console.WriteLine(test1.Test()); // Prints "hello"
var test2 = new Testing<Class2>();
Console.WriteLine(test2.Test()); // Prints "world"
}
}
}
在此代码中,编译和工作实际上不需要 where T : BaseClass
,但您可能希望保留它以明确它仅应与 [=21 一起使用=]es 继承自 BaseClass
.
由于静态方法和泛型类型参数在 C# 中不能一起工作(感谢 Matthew Watson 链接到 Eric Lippet 的博客)而且我不想创建 T
的新实例只是为了能够调用方法,我会用属性。
[AttributeUsage(AttributeTargets.Class)]
class SomeStringAttribute : Attribute
{
public string SomeString { get; set; }
public SomeStringAttribute(string s)
{
SomeString = s;
}
}
[SomeString("hello")]
public class Class1
{
}
[SomeString("world")]
public class Class2
{
}
public class Testing<T>
{
public void Test()
{
string s =
((SomeStringAttribute)typeof(T).GetCustomAttributes(typeof(SomeStringAttribute),
false)[0]).SomeString;
}
}