System.Reflection.PropertyInfo.GetValue() - Downgrading .NET version - method signature missing - c#

To get a .NET console application running on Server 2003, we're having to downgrade one of our core libraries from .NET 4.5 to 4.0. Among other things, this library has a class that performs some reflection, cycling through an object's properties to get their values using Propertyinfo.GetValue()
According to the MSDN documentation, PropertyInfo.GetValue (Object) is in .NET 4.5 only. In .NET 4.0, this method exists, but in the form PropertyInfo.GetValue (Object, Object[]) (the extra parameter is to know how to handle indexed properties).
If we're to downgrade this code, we need to know what happens when PropertyInfo.GetValue Method (Object) encounters an indexed property, so we can mirror this functionality using PropertyInfo.GetValue Method (Object, Object[]). Can anyone help?

The documentation isn't clear about this, but inspecting the implementation in a decompiler shows that property.GetValue(obj) just calls property.GetValue(obj, null) without any checks whatsoever, and without catching any exceptions whatsoever. Any exception that you would get from property.GetValue(obj) is therefore exactly the exception that you would get from property.GetValue(obj, null), and you should have no problems updating your calls.

Related

Find all extension methods that conflicts with built-in method of the type

I have just updated my project from .NET Standard 2.0 to 2.1 (Due to a Unity3D Version Update), and with this update, a lot of methods that I previously implemented with extension methods are now built-in to the type itself. (e.g. TryGetValue for KeyedCollection)
Unfortunately, some of my extension methods share the same name as the C# implementation, but have different behaviours. (e.g. my TryGetValue won't throw an exception when a null key is given, but .Net 2.1's will throw). As such, I am having a lot of runtime errors.
I wonder if there's any way to get the compiler to generate a warning for all of the extension methods that conflict with the base class?
Currently, I am finding all extension methods through finding all (this , and then checking if it has any usages (Code metrics display from Rider)
But this is taking a really long time and I want to see if there's a better way.

.net core COM with Excel range

I have an issue where I am unable to access the Name object of a range. So, I am calling Excel.Application.get_Range() and passing in a name. It returns a non-null object and I can access methods. However, Range.Name returns a System.__ComObject(). If I try to access Range.Name.Name I get an exception similar to the following
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ''System.__ComObject' does not contain a definition for "Name"
Also, this project is one that I migrated from .net Framework to .net Core. The exact same code works in Framework. I have tried various methods of trying to determine what the underlying type is (because it does not appear to be a Name object). ITypeInfo returns that the typename is Name so it does appear to be a name object.
I'm at a loss as to why I'm unable to access Name.Name in .net core. When I check the .net Core Excel sample they never access a name object.
Also, I am able to access other COM objects like ListObject just fine.
I figured out what the issue is. For anyone else running into this with .net Core, the problem is that .net core appears to not support dynamic types through COM automatically. This is discussed in the following github issue Github issue about dynamic not working. Where it talks about why this doesn't work.
Basically, for my specific case I needed to cast the type manually and then it worked. This also impacts use of IEnumerable COM types needing to be cast directly to IEnumerable before iterating through them.
Assuming you have the following using statement.
using Excel = Microsoft.Office.Interop.Excel;
I had to turn the following:
return range.Name.Name;
into
return ((Excel.Name)range.Name).Name;
As an aside, I also noticed that if you copy/paste the cast above into the debug watch window it does not display properly and instead shows an exception about an invalid cast.

NET Native Tool Chain for UWP throws NotImplementedException in Portable Libraries

We have quite a large shared project, which has 3 heads so to speak:
Windows.UWP
Windows.Desktop81
Windows.Phone81
These three each reference Portable Libraries which target both Windows 8.1 and Windows Phone 8.1.
When compiling the UWP project using the native tool chain, the portable libraries cannot access any type information so they can’t perform any reflection.
The method which is failing is a generic one, and inspects typeof(T) to do various operations depending on what type it is.
First line that throws an System.NotImplementedException is:
If (typeof(T).IsArray)
In this case, T is System.String, and if I break the debugger on the failing method and type into the immediate window of visual studio 2015, I get:
>> typeof(string).IsArray
An internal error has occurred while evaluating method System.Type.get_IsArray().
However, if I do the same in the App.OnLaunched method, that works fine. So the portable libraries cannot access any type information, even for system types like System.String.
I have tried adding platform directives for the Portable Libraries, but so far no luck.
Do you have any information regarding how to enable Portable Libraries to access type information.
I got a response back from Michal at Microsoft via email, explaining the root cause and how to get around it.
You seem to be hitting the same issue described here:
https://github.com/dotnet/corert/issues/3565, except the method in
question is Type.IsArray instead of ConstructorInfo.Invoke.
The problem is that the method Type.IsArray is declared as non-virtual
in the Portable Library contracts that your source code compiles
against, but it is virtual in the implementation assemblies used in
.NET Native. This is normally not a big problem because the C#
compiler pretty much always uses “callvirt” instruction to call the
method (even if it’s not virtual). The C# compiler shipped in Visual
Studio 2017 started doing an optimization that if a method is not
virtual and the 'this' passed to the method is known not to be null,
it uses “call” instead of “callvirt”. The result is that the code ends
up calling a method that should never have been called. The result of
the typeof() expression is known to be never null.
The good news is that we made IsArray non-virtual again as part of the
NetStandard 2.0 effort. The bad news is that .NET Native with support
for NetStandard 2.0 hasn’t shipped yet.
You’ll need a workaround. The easiest I can think of is to add an
extension method and use that instead:
static class NetNativeWorkarounds
{
public static bool IsArray(this Type type) => type.IsArray;
}
Using the extension method avoids the C# compiler optimization because
this condition is not met (the compiler doesn’t know what type you’ll
pass to the extension method and has to do a callvirt to the
Type.IsArray method).

simpleinjector 3.0 not supporting RegisterManyForOpenGeneric

So I decided to upgrade my version of simpleinjector to 3.0, and all of a sudden I get a message:
'SimpleInjector.Extensions.OpenGenericBatchRegistrationExtensions.RegisterManyForOpenGeneric(SimpleInjector.Container, System.Type, params System.Reflection.Assembly[])' is obsolete: 'This extension method has been removed. Please use Container.Register(Type, IEnumerable) instead.
The documentation still has this method in there:
http://simpleinjector.readthedocs.org/en/latest/advanced.html
So I'm curious, what's the alternative to:
container.RegisterManyForOpenGeneric(typeof(IEventHandler<>),
container.RegisterAll,
typeof(IEventHandler<>).Assembly);
Ahh.. after scratching my head for several hours, I figured it out:
container.RegisterCollection(typeof(IEventHandler<>),
typeof(IEventHandler<>).Assembly);
RegisterCollection handles open generics as well. Maybe this should be documented somewhere.
EDIT:
I realized in the new documentation, the above code is not a direct translation from RegisterManyForOpenGeneric. All it did was solve my compilation, but it didn't register my handlers, I just checked it today.
Additional information: No registration for type
This is the correct version:
container.Register(typeof(IEventHandler<>),
new[] { typeof(IEventHandler<>).Assembly });
Using a RegisterCollection would require some extra code changes (from the document):
Because we register a collection, we can no longer call container.GetInstance>(). Instead instances can be retrieved by having an IEnumerable> constructor argument or by calling container.GetAllInstances>().
Which I haven't done, and don't really need to do since I don't have mixed open-generic and non-generics. But I'll explore this more in the future if I wanna revamp my project.

Same code acts differently on different machines - what might be the cause ? (CLR version issue ?)

I just finished debugging a problem, where our program crashed on a production server, but never on development machines.
I have made this small program, which I could reproduce the issue with:
using System;
using System.Collections.Generic;
using System.Linq;
namespace RunTimeBug
{
class Program
{
static void Main(string[] args)
{
var coll = new Collection {{"Test", new Data()}, {"Test2", new Data()}};
var dataSequence = coll.Cast<Data>().ToList();
Console.WriteLine(dataSequence.Count);
}
}
class Collection : Dictionary<string,Data>, IEnumerable<Data>
{
public new IEnumerator<Data> GetEnumerator()
{
foreach(var v in Values)
yield return v;
}
}
class Data { }
}
When running on my machine, this code prints "2". When running on the production server, it fails with the following exception:
Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.KeyValuePair
`2[System.String,RunTimeBug.Data]' to type 'RunTimeBug.Data'.
at System.Linq.Enumerable.<CastIterator>d__b0`1.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at RunTimeBug.Program.Main(String[] args)
The only difference I can find on these machines, is that the CLR Runtime is version 2.0.50727.4927 on machines, where it works, and version 2.0.50727.1433 on machines where it does not work.
As far as I can tell, the Cast extension method gets the wrong version of IEnumerable on the older machines, but the "right" one on newer machines.
Can anyone explain why I am seeing this ? What has changed between the 2 CLR Runtime versions, that might be causing this ?
Please note, I have already deployed a fix, and I am aware that the Collection class in the code above is poor design because it implements 2 different IEnumerable's. This was found "in the wild" in our product, so I would really like to know the exact cause.
I think the fix mentioned in the article that Jason linked to has something to do with your code working on later releases. I don't have access to the pre-SP1 version of Enumerable.Cast so it is a bit hard to guess.
One thing's for sure: once the code makes it into Enumerable.CastIterator() then it is guaranteed not to work in your case. That sets up an iterator to convert IEnumerable to IEnumerable<> and that iterator calls GetEnumerator() to initialize the iterator block. That can only call Dictionary.GetEnumerator(), not yours. Casting the KeyValuePair that this IEnumerable returns to Data will of course fail, as the exception tells you.
The current version of Enumerable.Cast() first tries to cast IEnumerable to IEnumerable<>. And that works. CastIterator() isn't used.
CLR version 2.0.50727.1433 is .NET 2.0 SP1 whereas CLR version 2.0.50727.4927 includes .NET 3.5 SP1 (for reference, see the Version History of the CLR).
The behavior of Enumerable.Cast changed from .NET 3.5 to .NET 3.5 SP1 as outlined here.
Why do you think there is a "right" version to pick? Personally I'd be happy for it to throw am ambiguous compiler error at this point. Rather than Cast, perhaps just a regular cast?
var dataSequence = ((IEnumerable<Data>)coll).ToList();
Cast<T> casts the data, not the variable.
BTW, are you recompiling for each? I'm wondering if the issue is the compiler rather than the CLR or BCL.

Categories