Is GetFields supported in a PCL? - c#

I am trying to implement an enumeration class found at https://github.com/jbogard/presentations/blob/master/WickedDomainModels/After/Model/Enumeration.cs.
In the following code, I am getting a compile error that GetFields cannot be resolved.
public static IEnumerable<T> GetAll<T>() where T : Enumeration
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
return fields.Select(info => info.GetValue(null)).OfType<T>();
}
According to http://msdn.microsoft.com/en-us/library/ch9714z3(v=vs.110).aspx, this method is supported in Portable Class Libraries.
My library is targeting .NET for Windows Store apps, .NET Framework 4.5 and Windows Phone 8.
Any idea of what is going on here?
Solution
public static IEnumerable<T> GetAll<T>() where T : Enumeration
{
var type = typeof(T);
var fields = type.GetRuntimeFields().Where(x => x.IsPublic || x.IsStatic);
return fields.Select(info => info.GetValue(null)).OfType<T>();
}
public static IEnumerable GetAll(Type type)
{
var fields = type.GetRuntimeFields().Where(x => x.IsPublic || x.IsStatic);
return fields.Select(info => info.GetValue(null));
}

To add to Damien's answer, in .Net for Windows Store Apps, you can use the following extension method:
using System.Reflection;
var fields = type.GetRuntimeFields();
http://msdn.microsoft.com/en-us/library/system.reflection.runtimereflectionextensions.getruntimefields.aspx
This seems to be the equivalent of the GetFields method for the .Net Framework.
This method returns all fields that are defined on the specified type,
including inherited, non-public, instance, and static fields.

Just because a method says it's supported in Portable Class Libraries doesn't mean that it's supported for all possible targets. If you look at the help for the Type class, it lists each member and shows icons for each supported system.
In this case, you'll notice that there's no green shopping bag icon next to GetFields - it's not supported in Windows Store apps, and so as long as you include Windows Store in your set of supported targets for the PCL, it will not be available.
Another way to put it is - in the Version Information block for the methods, if they're supported for Windows Store, there'll be a specific section saying about it. Compare GetGenericTypeDefinition:
.NET Framework
Supported in: 4.5, 4, 3.5, 3.0, 2.0
.NET Framework Client Profile
Supported in: 4, 3.5 SP1
Portable Class Library
Supported in: Portable Class Library
.NET for Windows Store apps
Supported in: Windows 8
to GetFields
.NET Framework
Supported in: 4.5, 4, 3.5, 3.0, 2.0, 1.1, 1.0
.NET Framework Client Profile
Supported in: 4, 3.5 SP1
Portable Class Library
Supported in: Portable Class Library
For Windows Store apps, for reflection, you should switch to using the TypeInfo class - but note that it still doesn't, specifically, support the GetFields method.

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.

Using CallerMemberName attribute in a portable library

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
...

Why does GetExportedValues<T>() no longer work after using the .net 4 final version of MEF?

I have been developing a managed extensibility framework application for the last several months using the community preview. I have been using the GetExportedValues() method and the PartCreationPolicy(CreationPolicy.NonShared) to fake a class factory (since only the silverlight version supports a factory). This was working great until I upgraded the project to use .net 4.0. There is no error, it just doesn't work.
So why did this code stop working? The code follows:
The factory method:
public static IEnumerable<DataActionBase> GetActionsFromDirectory(string PluginsFolder)
{
IEnumerable<DataActionBase> result = null;
var catalog = new DirectoryCatalog(PluginsFolder);
var container = new CompositionContainer(catalog: catalog);
result = container.GetExportedValues<DataActionBase>();
return result;
}
Example Export Class:
[Export(typeof(DataActionBase))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class AnAction : DataActionBase
{
....
}
Have you recompiled your extensions against .NET 4.0? If the extensions reference the codeplex preview version of MEF, then the .NET 4.0 MEF won't pick them up. This is because the export attribute would be coming from an assembly with a different strong name, which .NET 4.0 MEF knows nothing about.

Categories