Using CallerMemberName attribute in a portable library - c#

I have a portable library that targets Windows Phone 7.1+ and Windows Store apps (for WinRT), which uses the .net 4.5 framework.
I would like to use the new [CallerMemberName] attribute in it. However, VS2012 told me that this attribute is not available in my portable library (that seems normal because it's not available in a WP7.1 project).
Yet, I have found out that I can create my own attribute and the compiler will understand it like the real one, by using this snippet:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
public sealed class CallerMemberNameAttribute : Attribute { }
}
However, as soon as try to compile it, I get the error The type 'System.Runtime.CompilerServices.CallerMemberNameAttribute' exists in both 'Portable.dll' and 'mscorlib.dll'.
I understand the message, but I would like to know if there is a way to use [CallerMemberName] in my portable lib ? I might have missed something.

Use the BCL Portability Pack which provides these attributes for older versions.
This packages enables projects targeting .NET Framework 4, Silverlight 4 and 5, and Windows Phone 7.5 (including any portable library combinations) to use new types from later versions of .NET including:
CallerMemberNameAttribute
CallerLineNumberAttribute
CallerFilePathAttribute
...

Related

The new feature `required' of c# 11 have a compilation error in multi target project including net7,net472

Environment: vs 2022 v 17.4.0, with NET7 sdk installed
I have a multi target project net7.0;net472:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;net472</TargetFrameworks>
<LangVersion>11.0</LangVersion>
<!--others-->
</PropertyGroup>
I tried to use the new feature required in c# 11 as given below:
public class Person
{
public required int Id { get; set; }
}
I get a compilation error in net472:
Error CS0656 Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor'
Error CS0656 Missing compiler required member 'System.Runtime.CompilerServices.RequiredMemberAttribute..ctor'
My workaround solution is using conditional compilation as:
public class Person
{
public
#if NET7_0
required
#endif
int Id { get; set; }
}
What should I do to support the new features 'required' of c# 11 in a multi target project has net47x?
C# 11 language features is definitely not supported in Framework 4.7.2, from this table: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version it shows the default language version for NET Framework was C# 7.3, and this SO answer C# 8 was starting to have problems. C# 8 features in .NET Framework 4.7.2
As noted in the last link, some features may have been included, but was not officially supported.
If you absolutely have to multi-target, you either have to do your conditional compilation, or be mindful of what language features you use, limiting it to C# 7.3. You could try updating to NET Framework 4.8, but I think the most you will gain is support for C#8.
Just like it was the case with some new language features in older C# versions, this will work when you create the missing attributes yourself. For the required keyword you will need this attributes:
RequiredMemberAttribute
CompilerFeatureRequiredAttribute
IsExternalInit (for the init keyword in CompilerFeatureRequiredAttribute)
But since this is not officially supported, there might be problems either now or in the future.
You can use the PolySharp package to get these (and other) classes in your projects.

Is it possible to use the C#11 'required' modifier with .NET Framework 4.8 and .Net Standard 2.0?

I'd like to be able to use the new C#11 required modifier with .NET Framework 4.8 and .Net Standard 2.0.
I'm using Visual Studio 2022 version 17.4. Is this possible?
This is possible, but you will have to provide some types that the compiler needs in order to support it. You will also need to compile using Visual Studio 2022 version 17.4 or later, along with C# 11.
The necessary types are defined in .NET 7.0, but they don't all exist in earlier versions. These types are as follows:
static class IsExternalInit - Introduced with C# 9 and .NET 5.
class RequiredMemberAttribute - Introduced with C# 11 and .NET 7.
class CompilerFeatureRequiredAttribute - Introduced with C# 11 and
.NET 7.
These types must all be defined within the System.Runtime.CompilerServices namespace. They should also be declared as internal - if they are public and defined within a class library that is referenced by a .NET 7 project, you'll get multiply-defined errors.
You can declare them as follows - this must be included in every .NET 4.8 or .NET Standard 2.0 assembly that uses the required modifier:
using System.ComponentModel;
namespace System.Runtime.CompilerServices
{
#if !NET5_0_OR_GREATER
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit {}
#endif // !NET5_0_OR_GREATER
#if !NET7_0_OR_GREATER
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
internal sealed class RequiredMemberAttribute : Attribute {}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
public string FeatureName { get; }
public bool IsOptional { get; init; }
public const string RefStructs = nameof(RefStructs);
public const string RequiredMembers = nameof(RequiredMembers);
}
#endif // !NET7_0_OR_GREATER
}
namespace System.Diagnostics.CodeAnalysis
{
#if !NET7_0_OR_GREATER
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
internal sealed class SetsRequiredMembersAttribute : Attribute {}
#endif
}
Note the use of the #if directives to support different targets.
(Incidentally, the declaration of internal static class IsExternalInit {} also enables the use of the record type and the init only property setter feature that was introduced in C# 9.)
Once you've added the definitions above to a .NET Framework 4.8 or .NET Standard 2.0 assembly, you can use the required modifier in that assembly even if the assembly is referenced by a different .NET Framework 4.8 or .NET Standard 2.0 assembly that doesn't provide those definitions (although you'd still need to provide the definitions in that other assembly if you want to use the required modifier for classes defined within that assembly).
Caveats:
All the .NET Framework 4.8 or .NET Standard 2.0 assemblies that use the required modifier must be compiled with Visual Studio 2022 version 17.4 or later.
Those projects will need to specify the language version as 11 or latest using <LangVersion>11</LangVersion> or <LangVersion>latest</LangVersion>.
You should not expose any public types that use the required modifier via a NuGet package.
This approach is NOT supported, and it could stop working with a future release (I think that's pretty unlikely, but it's not possible to guarantee that it will always work).
References:
C# 9 Records and Init Only Settings Without .NET 5 - This is a good blog article which goes into more detail on the caveats for exposing assemblies via NuGet. The article is about C# 9 records, but it is applicable here too.
Required modifer reference - The Microsoft documentation for required.

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.

How to use Windows Runtime classes in .NET Core libraries?

I'm developing a library for use with WPF and Windows 10. I'm running into issues getting it to compile on the latter. Here is some of the code:
project.json
{
"frameworks": {
"net46": {
"frameworkAssemblies": {
"WindowsBase": "4.0.0.0"
}
},
"netcore50": {
"dependencies": {
"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0"
}
}
}
}
Dependency.cs
using System;
using System.Collections.Generic;
#if NET46
using System.Windows; // .NET Framework 4.6
#elif NETCORE50
using Windows.UI.Xaml; // Windows 10 apps
#endif
public static class Dependency
{
public static DependencyProperty Register<T, TOwner>(string name, PropertyChangedCallback<T, TOwner> callback)
where TOwner : DependencyObject
{
// Code here....
}
}
While this compiles fine for net46 (which is the traditional .NET Framework), I'm having trouble getting it to work for netcore50 (which can be used by Windows 10 apps). For some reason, it looks like types like DependencyProperty or DependencyObject are not included in that configuration.
Is there a netcore50-compatible NuGet package I can install that contains these types, so I can use them from my library?
Thanks for helping.
EDIT: I just typed in DependencyProperty in VS and hit F12. It appears that the type lives in the Windows.Foundation.UniversalApiContract assembly, but there's no such package on NuGet.
Finally solved the problem on my own! (If you're looking for a quick answer, you may want to scroll down.)
I remembered by chance that the .NET Core GitHub repo had a bunch of WinRT-specific libraries, like System.Runtime.WindowsRuntime. So, I headed over there to see how they did it.
It appears they use some kind of internally-hosted "targeting pack", which contains a single Windows.winmd file (which holds all the types in the Windows Runtime), to achieve this affect. Unfortunately, the package is hosted on a private NuGet feed meant only for the .NET Core team, so I can't use it.
I've opened an issue about this on the CoreFX repo here, so I can petition Microsoft for an official solution to this problem. In the meantime, I've taken matters into my own hands. I've found all the different versions of Windows.winmd on my laptop, and uploaded them as NuGet packages. Here they are:
Target.Windows
Target.WindowsPhone
Target.WindowsRuntime
You can use them like this:
"frameworks": {
".NETPortable,Version=v4.5,Profile=Profile32": {
"dependencies": {
"Target.WindowsRuntime": "8.1.2"
}
}
}
After that, you'll be able to write something like this:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
public class MyApp : Application
{
public MyApp()
{
var button = new Button();
button.Content = "Hello, world!";
}
}
and it'll just work.
With .NET Core 3 and up (now in preview) there is a package you can install that includes most WinRT classes Microsoft.Windows.SDK.Contracts
WPF Isn't compatible with .net Core nor are W10 Universal Apps, to my knowledge only Console apps and web apps are currently compatible with .net core, you should be able to still use the the new code base with the new project system but you will need to remove .net core from your configuration in order to compile
if you want to use .net core with linux with a desktop app you will simply have to wait, or use a compatible windowed app framework ( if any are available yet), you should be able to use a cross platform framework base around html/js such as Electron or Cordova ( not sure on this one on whether there is a desktop app framework with Cordova)

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>

Categories