Я писал метод определения размера IEnumerable
без его перечисления, вот что у меня есть:
internal static class Extensions
{
internal delegate int GetLength(dynamic obj);
internal static readonly GetLength ByCount = o => o.Count;
internal static readonly GetLength ByLength = o => o.Length;
internal static readonly IDictionary<Type, GetLength> EnumerationSafeLengthGetters = new Dictionary<Type, GetLength>
{
{ typeof(Array), ByLength },
{ typeof(ICollection), ByCount },
{ typeof(ICollection<>), ByCount }, // ICollection<T> doesn't extend ICollection
{ typeof(IDictionary<,>), ByCount },
{ typeof(IReadOnlyCollection<>), ByCount }, // Matches System.Collections.Concurrent.ConcurrentStack`1 and System.Collections.Concurrent.ConcurrentQueue`1
{ typeof(string), ByLength }
};
internal static int GetEnumerationSafeLength(this IEnumerable values)
{
Type type = values.GetType();
return EnumerationSafeLengthGetters.FirstOrDefault(g => g.Key.IsAssignableFrom(type)).Value?.Invoke((dynamic) values) ?? -1;
}
}
Я начал получать странные исключения RuntimeBinderException в моих тестовых методах иНаконец-то я отследил это, связавшись с модификатором доступа для содержащегося типа:
public class PublicType { }
protected internal class ProtectedInternalType { }
protected class ProtectedType { }
internal class InternalType { }
private class PrivateType { }
public static void Main(params string[] args)
{
Console.WriteLine(new List<PublicType>() .GetEnumerationSafeLength()); // Prints "0"
Console.WriteLine(new List<ProtectedInternalType>().GetEnumerationSafeLength()); // Prints "0"
Console.WriteLine(new List<InternalType>() .GetEnumerationSafeLength()); // Prints "0"
// Both throw: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : 'object' does not contain a definition for 'Count'
Console.WriteLine(new List<ProtectedType>().GetEnumerationSafeLength());
Console.WriteLine(new List<PrivateType>() .GetEnumerationSafeLength());
}
Полное исключение, выдаваемое в случаях, когда содержащийся тип равен protected
или private
:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : 'object' does not contain a definition for 'Count'
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at CallSite.Target(Closure , CallSite , Object )
at Test.Extensions.<>c.<.cctor>b__5_0(Object o) in C:\Users\mrlore\Documents\Visual Studio 2015\Projects\Test\Test\Program.cs:line 13
at CallSite.Target(Closure , CallSite , GetLength , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at CallSite.Target(Closure , CallSite , GetLength , Object )
at Test.Extensions.GetEnumerationSafeLength(IEnumerable values) in C:\Users\mrlore\Documents\Visual Studio 2015\Projects\Test\Test\Program.cs:line 30
at Test.Test.Main(String[] args) in C:\Users\mrlore\Documents\Visual Studio 2015\Projects\Test\Test\Program.cs:line 50
at Test.Test.CallMain() in C:\Users\mrlore\Documents\Visual Studio 2015\Projects\Test\Test\Program.cs:line 55
Что я могу с этим сделать?Это ошибка?Я знаю, что dynamic
нельзя использовать для доступа к частным свойствам, но List<T>.Count
является общедоступным, так почему я не могу получить к нему доступ?Есть ли другой способ сделать это?