检测对象是否为 ValueTuple
Detect if an object is a ValueTuple
我有一个用例,我需要检查某个值是否为 C# 7 ValueTuple,如果是,则循环遍历每个项目。我尝试使用 obj is ValueTuple
和 obj is (object, object)
检查,但两者 return 都是错误的。我发现我可以使用 obj.GetType().Name
并检查它是否以 "ValueTuple"
开头,但这对我来说似乎很蹩脚。欢迎任何替代方案。
我也有获取每件商品的问题。我试图通过此处找到的解决方案获得 Item1
:How do I check if a property exists on a dynamic anonymous type in c#? 但 ((dynamic)obj).GetType().GetProperty("Item1")
return 为空。我希望我可以做一个 while
来获得每个项目。但这不起作用。我怎样才能获得每件物品?
更新 - 更多代码
if (item is ValueTuple) //this does not work, but I can do a GetType and check the name
{
object tupleValue;
int nth = 1;
while ((tupleValue = ((dynamic)item).GetType().GetProperty($"Item{nth}")) != null && //this does not work
nth <= 8)
{
nth++;
//Do stuff
}
}
结构在 C# 中不继承,因此 ValueTuple<T1>
、ValueTuple<T1,T2>
、ValueTuple<T1,T2,T3>
等是不同的类型,它们不继承自 ValueTuple
作为它们的基础。因此,obj is ValueTuple
检查失败。
如果您正在寻找具有任意类型参数的 ValueTuple
,您可以检查 class 是否为 ValueTuple<,...,>
,如下所示:
private static readonly Set<Type> ValTupleTypes = new HashSet<Type>(
new Type[] { typeof(ValueTuple<>), typeof(ValueTuple<,>),
typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
}
);
static bool IsValueTuple2(object obj) {
var type = obj.GetType();
return type.IsGenericType
&& ValTupleTypes.Contains(type.GetGenericTypeDefinition());
}
要根据类型获取子项目,您可以使用不是特别快的方法,但应该可以解决问题:
static readonly IDictionary<Type,Func<object,object[]>> GetItems = new Dictionary<Type,Func<object,object[]>> {
[typeof(ValueTuple<>)] = o => new object[] {((dynamic)o).Item1}
, [typeof(ValueTuple<,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2}
, [typeof(ValueTuple<,,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2, ((dynamic)o).Item3}
, ...
};
这会让你这样做:
object[] items = null;
var type = obj.GetType();
if (type.IsGeneric && GetItems.TryGetValue(type.GetGenericTypeDefinition(), out var itemGetter)) {
items = itemGetter(obj);
}
这是我解决问题的方法。 PCL 兼容扩展 class。特别感谢@dasblinkenlight 和@Evk 帮助我!
public static class TupleExtensions
{
private static readonly HashSet<Type> ValueTupleTypes = new HashSet<Type>(new Type[]
{
typeof(ValueTuple<>),
typeof(ValueTuple<,>),
typeof(ValueTuple<,,>),
typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>),
typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>),
typeof(ValueTuple<,,,,,,,>)
});
public static bool IsValueTuple(this object obj) => IsValueTupleType(obj.GetType());
public static bool IsValueTupleType(this Type type)
{
return type.GetTypeInfo().IsGenericType && ValueTupleTypes.Contains(type.GetGenericTypeDefinition());
}
public static List<object> GetValueTupleItemObjects(this object tuple) => GetValueTupleItemFields(tuple.GetType()).Select(f => f.GetValue(tuple)).ToList();
public static List<Type> GetValueTupleItemTypes(this Type tupleType) => GetValueTupleItemFields(tupleType).Select(f => f.FieldType).ToList();
public static List<FieldInfo> GetValueTupleItemFields(this Type tupleType)
{
var items = new List<FieldInfo>();
FieldInfo field;
int nth = 1;
while ((field = tupleType.GetRuntimeField($"Item{nth}")) != null)
{
nth++;
items.Add(field);
}
return items;
}
}
关于问题的部分"How can I get each item?"...
ValueTuple 和 Tuple 都实现了 ITuple
,其长度为 属性,索引器为 属性。因此,以下控制台应用程序代码向控制台列出了值:
// SUT (as a local function)
IEnumerable<object> GetValuesFromTuple(System.Runtime.CompilerServices.ITuple tuple)
{
for (var i = 0; i < tuple.Length; i++)
yield return tuple[i];
}
// arrange
var valueTuple = (StringProp: "abc", IntProp: 123, BoolProp: false, GuidProp: Guid.Empty);
// act
var values = GetValuesFromTuple(valueTuple);
// assert (to console)
Console.WriteLine($"Values = '{values.Count()}'");
foreach (var value in values)
{
Console.WriteLine($"Value = '{value}'");
}
控制台输出:
Values = '4'
Value = 'abc'
Value = '123'
Value = 'False'
Value = '00000000-0000-0000-0000-000000000000'
hackish one liner
type.Name.StartsWith("ValueTuple`")
(可扩展查看末尾的数字)
我有一个用例,我需要检查某个值是否为 C# 7 ValueTuple,如果是,则循环遍历每个项目。我尝试使用 obj is ValueTuple
和 obj is (object, object)
检查,但两者 return 都是错误的。我发现我可以使用 obj.GetType().Name
并检查它是否以 "ValueTuple"
开头,但这对我来说似乎很蹩脚。欢迎任何替代方案。
我也有获取每件商品的问题。我试图通过此处找到的解决方案获得 Item1
:How do I check if a property exists on a dynamic anonymous type in c#? 但 ((dynamic)obj).GetType().GetProperty("Item1")
return 为空。我希望我可以做一个 while
来获得每个项目。但这不起作用。我怎样才能获得每件物品?
更新 - 更多代码
if (item is ValueTuple) //this does not work, but I can do a GetType and check the name
{
object tupleValue;
int nth = 1;
while ((tupleValue = ((dynamic)item).GetType().GetProperty($"Item{nth}")) != null && //this does not work
nth <= 8)
{
nth++;
//Do stuff
}
}
结构在 C# 中不继承,因此 ValueTuple<T1>
、ValueTuple<T1,T2>
、ValueTuple<T1,T2,T3>
等是不同的类型,它们不继承自 ValueTuple
作为它们的基础。因此,obj is ValueTuple
检查失败。
如果您正在寻找具有任意类型参数的 ValueTuple
,您可以检查 class 是否为 ValueTuple<,...,>
,如下所示:
private static readonly Set<Type> ValTupleTypes = new HashSet<Type>(
new Type[] { typeof(ValueTuple<>), typeof(ValueTuple<,>),
typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
}
);
static bool IsValueTuple2(object obj) {
var type = obj.GetType();
return type.IsGenericType
&& ValTupleTypes.Contains(type.GetGenericTypeDefinition());
}
要根据类型获取子项目,您可以使用不是特别快的方法,但应该可以解决问题:
static readonly IDictionary<Type,Func<object,object[]>> GetItems = new Dictionary<Type,Func<object,object[]>> {
[typeof(ValueTuple<>)] = o => new object[] {((dynamic)o).Item1}
, [typeof(ValueTuple<,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2}
, [typeof(ValueTuple<,,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2, ((dynamic)o).Item3}
, ...
};
这会让你这样做:
object[] items = null;
var type = obj.GetType();
if (type.IsGeneric && GetItems.TryGetValue(type.GetGenericTypeDefinition(), out var itemGetter)) {
items = itemGetter(obj);
}
这是我解决问题的方法。 PCL 兼容扩展 class。特别感谢@dasblinkenlight 和@Evk 帮助我!
public static class TupleExtensions
{
private static readonly HashSet<Type> ValueTupleTypes = new HashSet<Type>(new Type[]
{
typeof(ValueTuple<>),
typeof(ValueTuple<,>),
typeof(ValueTuple<,,>),
typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>),
typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>),
typeof(ValueTuple<,,,,,,,>)
});
public static bool IsValueTuple(this object obj) => IsValueTupleType(obj.GetType());
public static bool IsValueTupleType(this Type type)
{
return type.GetTypeInfo().IsGenericType && ValueTupleTypes.Contains(type.GetGenericTypeDefinition());
}
public static List<object> GetValueTupleItemObjects(this object tuple) => GetValueTupleItemFields(tuple.GetType()).Select(f => f.GetValue(tuple)).ToList();
public static List<Type> GetValueTupleItemTypes(this Type tupleType) => GetValueTupleItemFields(tupleType).Select(f => f.FieldType).ToList();
public static List<FieldInfo> GetValueTupleItemFields(this Type tupleType)
{
var items = new List<FieldInfo>();
FieldInfo field;
int nth = 1;
while ((field = tupleType.GetRuntimeField($"Item{nth}")) != null)
{
nth++;
items.Add(field);
}
return items;
}
}
关于问题的部分"How can I get each item?"...
ValueTuple 和 Tuple 都实现了 ITuple
,其长度为 属性,索引器为 属性。因此,以下控制台应用程序代码向控制台列出了值:
// SUT (as a local function)
IEnumerable<object> GetValuesFromTuple(System.Runtime.CompilerServices.ITuple tuple)
{
for (var i = 0; i < tuple.Length; i++)
yield return tuple[i];
}
// arrange
var valueTuple = (StringProp: "abc", IntProp: 123, BoolProp: false, GuidProp: Guid.Empty);
// act
var values = GetValuesFromTuple(valueTuple);
// assert (to console)
Console.WriteLine($"Values = '{values.Count()}'");
foreach (var value in values)
{
Console.WriteLine($"Value = '{value}'");
}
控制台输出:
Values = '4'
Value = 'abc'
Value = '123'
Value = 'False'
Value = '00000000-0000-0000-0000-000000000000'
hackish one liner
type.Name.StartsWith("ValueTuple`")
(可扩展查看末尾的数字)