Enumerable.ToHashSet missing after framework upgrade - c#

Sketch of the usage scenario:
HashSet<SomeClass> nodesToCopy = someList.Where(SomePredicate).ToHashSet();
After I have upgraded my project from the target .net framework version 4.5 to 4.5.2, the method Enumerable.ToHashSet<> is suddenly not available anymore, which gives me errors. As I look into the documentation, Microsoft tells me that Enumerable.ToHashSet has been introduced with version 4.7.2.
Now I am a little bit confused because it has definitly been there (and working) with version 4.5, because I got no errors then. I also did not define the extension method myself, which has been confirmed by searching my old code (and otherwise an extension method would also be there when I switch the framework to 4.5.2). It is also clear that I have not missed any 'using' declarations or assembly references, because otherwise it would not have worked in the first place.
So, isn't 4.5.2 downward compatible to 4.5 ? And I can hardly imagine that Microsoft is tampering with history (at least not deliberately) with regards to version 4.7.2. Maybe they had ToHashSet as a 'hidden feature' in 4.5?
While I appreciate and understand foreseeable recommendations to use newer frameworks, I cannot do this at the moment for various reasons. And I think I need to change to 4.5.2 to incorporate an external dll which is also compiled for 4.5.2. Of course, one solution would be to implement ToHashSet myself.
But my point is not primarily the solution, but I want to understand ('cause knowledge is power) if there is a sane reason for ToHashSet to once be there and then again not in a later framework. Does anyone know what is going on here?
Update 20230209: I have set up a minimal 'working' example. ToHashSet produces the desired (but unexpected, as for framework 4.5) result, no errors. Just a plain console application:
using System;
using System.Linq;
using System.Collections.Generic;
namespace ToHashSetError
{
class Program
{
public static void Main(string[] args)
{
List<string> myList = new List<string>(new[]{"abcd","efg"});
HashSet<string> myHashSet = myList.ToHashSet();
Console.Write(String.Join(",", myHashSet));
Console.Write("\r\nPress any key to continue . . . ");
Console.ReadKey(true);
}
}
}
with the default assembly references SharpDevelop chooses, and with target framework 4.5 set:
What is remarkable, the same code when trying to compile it on a different PC, fails as expected with an error (ToHashSet not available) for framework 4.5. So it is obviously something rooted in the jungle of different framework installations. But how to fix this, or even just find out the reason?

Related

Path.GetDirectoryName changes in .NET Framework 4.6.2

Recently I updated my unit test project from .NET 4.5.1 to a higher version. When you update .net to 4.6.2 and higher there are changes in Path.GetDirectoryName behavior. It is known that in 4.6.2 version it become stricter, not allowing uri syntax anymore. But in my case I observe that it becomes LESS strict in some cases. The wrong path "/:\test\temp.txt" which caused an exception before now passes. Is this also a known issue or are there reasons for it?
using System.IO;
...
string dir = Path.GetDirectoryName("/:\\test\\temp.txt");
// .net 4.6.1: throws ArgumentException: The path is not of a legal form.
// .net 4.6.2 and above: dir = \:\test

Why WindowsIdentity class is not visible in .NET Core

Having the code below in VisualStudio 2017 .NET Core 2.0 Console App
using System;
using System.Security.Principal;
namespace smallTests
{
class Program
{
static void Main(string[] args)
{
var identity = WindowsIdentity.GetCurrent();
}
}
}
Why am I getting the error:
The name 'WindowsIdentity' does not exist in the current context
If I can see this class in .NET Core 2.0 library in .Net Core docs ?
Same code works in .NET Console app.
[EDIT]
#Will #JohnnyL Commented that I do not refer, System.Security.Principal.Windows.dll, that is true.
But I am curious why it is not working, because
in .NET 4.6.1 project (where class WindowsIdentity is visible) I also do not refer this System.Security.Principal.Windows.dll specifically. However i refer System.dll.
I always thought that it works like namespace hierarchy. For instance, when I refer to
System.Security.Principal.dll
i can use class which is in
System.Security.Principal.Windows.dll.
Am I wrong?
I added System.Security.Principal.dll to .NetCore solution by hand but it still does not work.
[EDIT2]
#Will Thank you a lot for expaining the subject it helped me a lot.
I tried to figure out is WindowsIdentity compatible with Core and it seems that it is please see:
in this apisof.net in Declarations area i can see that WindowsIdentity is in .Net Core 2.0 System.Security.Principal.Windows, Version=4.1.1.0, PublicKeyToken=b03f5f7f11d50a3a
but i do not have System.Security.Principal.Windows.dll in references, should I add it? If yes from where?
in .NET Core api reference i see this class in the list (what is the purpose of that listing if it is not compatible with core?
I also find information about that class in that link
Am I looking in wrong places?
Microsoft announced Windows Compatibility Pack for .NET Core a few weeks ago,
https://blogs.msdn.microsoft.com/dotnet/2017/11/16/announcing-the-windows-compatibility-pack-for-net-core/
And by analyzing the source code of System.Security.Principal.Windows.csproj and the commit adding it,
https://github.com/dotnet/corefx/blob/master/src/System.Security.Principal.Windows/src/System.Security.Principal.Windows.csproj
My conclusion is that this is also part of the Windows only compatibility libraries, so can only be used on Windows.
To add that to your project, open your csproj and add a PackageReference tag for System.Security.Principal.Windows manually (or use Visual Studio's NuGet Package Manager).

Referencing between NetStandard and .Net Framework

I'm trying to get .Net Framework and NetStandard assemblies to communicate with each other (to learn what is possible). I currently have four projects, two Framework 4.5.2 projects and two NetStandard1.2 projects:
Framework452.Library
NetStandard12.CentralLibrary
NetStandard12.BaseLibrary
Framework452.Tests
The referencing structure is:
Framework452.Tests references NetStandard12.CentralLibrary: working by adding the NetStandard.Library nuget package to Framework452.Tests.
NetStandard12.CentralLibrary references NetStandard12.BaseLibrary: working without modification.
NetStandard12.CentralLibrary references Framework452.Library: Not working, even when Framework452.Library has the NetStandard.Library nuget package installed.
Can NetStandard projects reference Framework projects? If so, what do I need to do to get them to communicate? At the moment I can add the reference, but it is not visible to the code.
Update
I recreated the solution and added the code below, which when I try to compile gives the following error from the Framework452.Tests project:
Error CS0006: Metadata file
'~\TryNETStandard\NetStandard12.CentralLibrary\bin\Debug\netstandard1.2\NetStandard12.CentralLibrary.dll'
could not be found.
namespace Framework452.Library
{
public class Returner452 {
public static bool ReturnTrue() { return true; }
}
}
using Xunit;
namespace Framework452.Tests
{
public class Class1 {
[Fact]
public void FrameworkTest() {
Assert.True(NetStandard12.CentralLibrary.Class1.Return452());
}
[Fact]
public void NetStandardTest() {
Assert.True(NetStandard12.CentralLibrary.Class1.Return12());
}
}
}
namespace NetStandard12.BaseLibrary
{
public class Returner12 {
public static bool ReturnTrue() { return true; }
}
}
using Framework452.Library;
using NetStandard12.BaseLibrary;
namespace NetStandard12.CentralLibrary
{
public class Class1
{
public static bool Return452() { return Returner452.ReturnTrue(); }
public static bool Return12() { return Returner12.ReturnTrue(); }
}
}
According to this page https://learn.microsoft.com/en-us/dotnet/standard/net-standard#net-platforms-support you should be able to achieve your purpose because .NET Standard 1.2 support .NET Framework 4.5.1 (UPDATE: This statement is not 100% correct. Please see the Update section below.)
I tried to set up a solution in VS 2017 and set the references as you described. Here is the result.
and this is the Class1.cs in NetStandard12.CentralLibrary
The code compiles fine without any errors.
Note: your code may fail if the Framework452.Library uses an API that is not supported by .NET Standard 1.2 (e.g Winforms, Win32 API or any Microsoft proprietary library that does not make sense for cross platform).
I recommend this youtube playlist on the .NET standard introduction from one of the MSFT https://www.youtube.com/watch?v=YI4MurjfMn8&list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY
In .NET Standard - Checking Compatibilty , he recommended tools to help you find out what API is not supported in the .NET Standard.
Thing will become easier with .NET Standard 2.0 and 'compat shim'
UPDATE:
After trying again with more data provided by the question, it's true that a library targeting (depends) .NET Standard could not depend on a library that target .NET Framework. For some strange reason, the compiler allows me to compile the example that I gave above. This could be a bug in tooling.
After a little more research, I found a good example demonstrate the relationship between NetStandard and NetFramework: How .NET Standard relates to .NET Platform.
The graph here show the dependencies
According to the graph, there is no way a library that depends on .NET Standard could see/use the .NET framework implementation.
When .NET Standard 2 is released, this may change a little bit and you could reference .NET Framework via Compatibility Shim. See this video for more in-depth explanation https://youtu.be/vg6nR7hS2lI?list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY&t=169
No, .NET Standard projects cannot reference framework projects.
.NET Standard projects need to be usable across platforms, forcing a dependency on the .NET framework by referencing an assembly targeting it makes this impossible.
Note that with some of the magic Microsoft is doing with .NET Standard 2.0 this is less true but the overall idea still stands.

Can I make a preprocessor directive dependent on the .NET framework version?

Here's a concrete example of what I want to do.
Consider the string.Join function. Pre-.NET 4.0, there were only two overloads, both of which required a string[] parameter.
As of .NET 4.0, there are new overloads taking more flexible parameter types, including IEnumerable<string>.
I have a library which includes a Join function that does essentially what the .NET 4.0 string.Join function does. I was just wondering if I could make this function's implementation dependent on the .NET framework being targeted. If 4.0, it could simply call string.Join internally. If 3.5 or older, it could call its own internal implementation.
Does this idea make sense?
If it does make sense, what's the most logical way to do it? I guess I'm just assuming a preprocessor directive would make the most sense, since a call to string.Join with an IEnumerable<string> parameter won't even compile when targeting a .NET version older than 4.0; so whatever approach I use would have to take place prior to compilation. (Checking the Environment.Version property at runtime, for example, wouldn't work.)
You can take a look at another question on Stack Overflow that illustrates how to set conditional constants through the project file's XML:
Detect target framework version at compile time
Then using that you can determine if you should use the .NET 4 overloads or your own library.
At some point (not sure when), Microsoft added predefined symbols for .NET versions into the MSBuild build system. Everything here works if you are using MSBuild from the .NET 5+ SDK (even if the project you are building with that SDK is using a much older target framework).
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives
Target Frameworks
Symbols
Additional symbols available in .NET 5+ SDK
.NET Framework
NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20
NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, NET40_OR_GREATER, NET35_OR_GREATER, NET20_OR_GREATER
.NET Standard
NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0
NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, NETSTANDARD1_1_OR_GREATER, NETSTANDARD1_0_OR_GREATER
.NET 5+ (and .NET Core)
NET, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0
NET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, NETCOREAPP1_1_OR_GREATER, NETCOREAPP1_0_OR_GREATER
This allows you to, for example, do this:
#if NET5_0_OR_GREATER
Console.WriteLine("This is .NET 5 or later.");
#elif NETCOREAPP
Console.WriteLine("This is an older version of .NET Core.");
#elif NETFRAMEWORK
Console.WriteLine("This is the legacy .NET Framework.");
#else
Console.WriteLine("This is something else.");
#endif
Yes, I think it makes sense (for your particular case, since the change is relatively minor), though obviously that sort of thing could scale out of control fairly quickly.
IMHO, the most logical way to go about it would be to create different solution/project configurations for each version, then define a custom symbol (say, NET40) in your 4.0 configurations, then use that with an #if. I'm not certain if configurations will allow you to change the runtime version (that would obviously be the perfect solution), but your worst-case is having to change the version manually.
EDIT: I just saw the answer linked to in Joshua's answer, and that seems like a more streamlined solution, but I'll leave this here anyway, since it does, strictly speaking, answer the question.
You can prepare your code for .NET 4.0 and write the similar code for the .NET 3.5 base on framework detection.
#if NOT_RUNNING_ON_4
public static class GuidExtensions
{
public static bool TryParse(this string s, out Guid result)
{
if (s.IsNullOrEmpty())
return null;
try
{
return new Guid(s);
}
catch (FormatException)
{
return null;
}
}
}
#else
#error switch parsing to .NET 4.0
#endif
And put his line to your *.csproj
<DefineConstants Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">NOT_RUNNING_ON_4</DefineConstants>

Is it possible to use DLR in a .NET 3.5 website project?

I'm trying to evaluate an expression stored in a database i.e.
"if (Q1 ==2) {result = 3.1;} elseif (Q1 ==3){result=4.1;} else result = 5.9;"
Rather than parsing it myself I'm trying to use the DLR. I'm using version .92 from the Codeplex repository and my solution is a .NET 3.5 website; and I'm having conflicts between the System.Core and Microsoft.Scripting.ExtenstionAttribute .dll's.
Error =
{
Description: "'ExtensionAttribute' is ambiguous in the namespace 'System.Runtime.CompilerServices'.",
File: "InternalXmlHelper.vb"
}
At this time I cannot upgrade to .NET 4.0 and make significant use of the .net 3.5 features (so downgrading is not an option).
Any help greatly appreciated.
The solution is to type forward ExtensionAttribte into System.Core.dll. We've made 3 different versions of this assembly (for the 3 different versions that we've shipped w/ various IronPython versions) and attached to them on this bug on IronPython's CodePlex site.
You'll need to download them and check the versions on them and replace the one that matches the version in the CodePlex release you're using.
I might be to complex thinking right now and more easy solutions exists, but this jumped into my mind as a possibility;
Have you considered building a runtime class using the CodeDom, instanciating one, executing a method on it (with the expression as its implementation, which is more like code-snippets than a pure expression) and then retrieving the 'result' value from that class instance via a public property?

Categories