Das ich keinen festen Type habe macht mich fertig!
Bezugnehmend auf meine letzte Frage: ValueConverter-Parameter Type übergeben ergibt sich jetzt noch ein Problem.
Ich möchte den ValueConverter in eine Bibliothek übernehmen. Diese hat als Zielframwork .NET Framework 4.5 sprich Sprachversion C# 7.3.
Bei dem Erstellen des Enum-Eintrages aus dem übergebenen Namen und dem übergebenen Type:
if (Enum.TryParse(type, name, out object enumValue)) // Konnte aus dem Enum-Type und dem Enum-Namen ein Enum-Eintrag erstellt werden?
wird mir der Fehler:
Fehler
Fehler: CS8370 Das Feature "not-Muster" ist in C# 7.3 nicht verfügbar.
angezeigt.
Also habe (wollte) ich die TryParse-Methode der Version 7.3 benutzen.
Enum enumValue;
Enum.TryParse(name, out enumValue);
Dabei stieß ich auf das Problem, dass ich für die out-Variable eine Deklaration mit dem speziefischen Type der Enumeration benötige.
Fehler
Fehler: CS0453 Der Typ "Enum" muss ein Non-Nullable-Werttyp sein, wenn er als TEnum-Parameter im generischen Typ oder in der generischen Methode "Enum.TryParse<TEnum>(string, out TEnum)" verwendet werden soll.
Mit type hätte ich zwar den spezifischen Type, aber den kann ich ja nicht als Variable casten.
Type type = (Type)parameter; // Ziel Enum-Type "holen"
(type)enumValue; // geht nicht
object enumValue = Activator.CreateInstance(type); // geht nicht, da objekt wieder nullable ist
Wie bekomme ich denn aus einer Type-Variable eine Type Angabe???
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von perlfred am .
Warum verwendest du noch .NET 4.5?
Das hat seit einiger Zeit keinen Support mehr.
Am besten ist ein Update auf .NET 4.8, dann hast du solche und weitere Probleme nicht mehr.
Wird ja vielleicht seinen Grund haben so eine Steinzeit-Version zu verwenden, welche auch immer.
-------
Der Fehlercode CS8370 ist leider ein Sammelcode, dahinter stecken dutzende von potentiellen Fehlern. In der Regel immer Fehler, die darauf schließen, dass man Sprachfeature verwendet, die aufgrund der Version nicht zur Verfügung stehen.
Die Lokalisierung des Fehlertexts ist fürchterlich; ich kann mir kein Reim draus machen, welche originale, englische Fehlermeldung hier gemeint sein könnte. Eine der vielen Gründe, wieso man mit lokalisierten SDKs nicht arbeiten sollte: Fehlersuche über Google endet meist ohne Treffer.
Aber wenigstens hast die Ursache gefunden.
Ein Enum ist keine klassische Instanz, sondern im Endeffekt "nur" ein "statischer" Programmierhelfer. Daher kann man logischerweise keine Instanzen von Enums erzeugen. Sie unterliegen auch nicht den Regeln von instantiierbaren Typen.
Das würde auch den Sinn von Enums völlig untergraben; gilt nicht nur für .NET so, sondern zB auch in C, C++....
Ich hab das kurz mit .NET 4.6 getestet (älter geht bei mir nicht mehr); funktioniert einwandfrei mit TryParse:
using System;
namespace ConsoleApp1
{
public enum MyEnum { No, Yes }
internal class Program
{
static void Main(string[] args)
{
string input = "no";
MyEnum result;
if (Enum.TryParse(input, true, out result))
{
Console.WriteLine("My Enum is: " + result); // Output: My Enum is: No
}
else
{
Console.WriteLine("Unable to parse: " + input);
}
}
}
}
Zitat
Dabei stieß ich auf das Problem, dass ich für die out-Variable eine Deklaration mit dem speziefischen Type der Enumeration benötige.
Das ist in der Form korrekt, und kannst Du ohne weiteres auch nicht ändern. Sie unterliegen halt anderen Regeln als Typen.
Hinter einem Type steckt eine Identität mit Namespace, Name etc etc... Ein Enum ist einfach nur ein int oder ein Byte. Ein Enum hat keine Identität, dessen Ursprung dafür verwendet werden könnte.
Hab Dein anderes Thema mal so überflogen; liest sich als ob ne Eierlegende Wollmilchsau umgesetzt werden soll. Da wirds mit Enums schwer.
Ich verwende diese Bibliothek in vielen meiner Projekte und habe (noch) nicht den Überblick was für Auswirkungen eine Veränderung haben kann.
Mir ist überhaupt der Zusammenhang zwischen .NET-Version und C#-Sprachversion nicht so ganz klar.
Ich weiß, dass ich theoretisch die Sprachversion (unabhängig von der .NET-Version???) in der VS-Projekt-Datei einstellen kann.
In der jetzigen Fehlermeldung wird eigentlich die C#-Sprachversion 9.0 vorgeschlagen.
Fehler
Fehler: CS8370 Das Feature "not-Muster" ist in C# 7.3 nicht verfügbar. Verwenden Sie Sprachversion 9.0 oder höher.
Die Sprachversion C# 9.0 verlangt lt. (m)einer Übersicht eigentlich sogar die .NET 5.0 Version. Bei Umstellungen meiner Projekte auf .NET Core-Versionen, hatte ich allerdings schon masive Probleme mit verschiedenen NuGet-Paketen! Deshalb bin ich da jetzt etwas vorsichtiger geworden.
Wie kommst du überhaupt auf .NET 4.8? Die C# 8.0 Sprachversion beginnt ab .NET Core3.0.
Mal unabhängig von all diesen Zusammenhängen möchte ich natürlich auch mein Wissendefizit, was diese Grundlagen betrifft, reduzieren. Es muss ja auch vorher eine Lösung für dieses Problem (Enum.TryParase mit Variable als Type) geben!!!
C# 8 wird von .NET Core und .NET Standard unterstützt. .NET Standard wiederum kannst Du auch in gewissen Versionen in .NET Framework verwenden. .NET Standard
Deine .NET Version is aber sooo dermaßen alt, da ist nix mehr zu machen.
Zitat
Es muss ja auch vorher eine Lösung für dieses Problem (Enum.TryParase mit Variable als Type) geben!!!
Nicht in der Form, weil Enums keine Types sind. Bei Enums ist immer notwendig, dass das Ziel-Enum bekannt ist.
Die C#-Version setzt keine .NET-Version voraus, einige Features dagegen schon, das sind häufig nur Compiler-Features, die bestimmte Klassen/Structs anhand Namespace und Namen suchen.
Viele Features kann man sich also "nachrüsten", indem man diesen Code anbietet, häufig gibt's dafür schon fertige NuGet-Packages, oder Du kopierst dir den Code selber.
Ob man das machen will/sollte, ist eine andere Frage, aber bei einfachem Code (z.B. die NullableReferenceType-Attribute) sehe ich da überhaupt kein Problem.
Besser wäre aber, Du bringst alle deine Projekte auf 4.8 und in deinem gemeinsam genutzten Projekt arbeitest Du mit .NET Standard 2.0 - .NET 6/7 wäre natürlich noch besser
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Palladin007 am .
Am besten ist ein Update auf .NET 4.8, dann hast du solche und weitere Probleme nicht mehr.
Leider ist dem nicht so. Ich habe meine Bibliothek jetzt auf die .Net Framwork-Version 4.8 geändert. Hier ist das Feature "not-Muster" auch noch nicht verfügbar. Scheinbar ist die Fehlermeldung:
Fehler
Fehler: CS8370 Das Feature "not-Muster" ist in C# 7.3 nicht verfügbar. Verwenden Sie Sprachversion 9.0 oder höher.
doch präzise und das Feature ist erst ab der C# Sprachversion 9.0, das würde lt. der Zuordnungstabelle .Net 5.0 bedeuten, verfügbar!
Mit einer Erhöhung innerhalb der .Net Framework-Versionen hätte ich kein Problem. Ein Wechsel der Bibliothek zu den Core-Versionen erscheint mir aber doch recht aufwendig, da ich dann viele (nicht alle [die Neueren habe ich schon in Core 3.1 oder .Net 5/6/7 erstellt]) Projekte "hochziehen" müsste, da diese Bibliothek in fast allen Projekten benutzt wird.
Die Aussage von Palladin007:
Zitat
Viele Features kann man sich also "nachrüsten", indem man diesen Code anbietet, häufig gibt's dafür schon fertige NuGet-Packages, oder Du kopierst dir den Code selber.
muss ich erst hinterfragen. Wie (woher?) kann ich den Code für ein Features nehmen, den ich kopieren könnte.
Die letzte Möglichkeit besteht natürlich darin, eine neue Bibliothek mit der passenden C# Sprachversion zu erstellen und diese jetzt und in der Zukunft zu verwenden.
Leider ist dem nicht so. Ich habe meine Bibliothek jetzt auf die .Net Framwork-Version 4.8 geändert. Hier ist das Feature "not-Muster" auch noch nicht verfügbar.
Stell auf Englisch um (ggf. reicht das bei Visual Studio?), ich kann mit der Fehlermeldung auch nichts anfangen.
Zitat von perlfred
muss ich erst hinterfragen. Wie (woher?) kann ich den Code für ein Features nehmen, den ich kopieren könnte.
Was Du brauchst, musst Du allerdings selber herausfinden, z.B. mit https://sharplab.io/ (Rechts oben "Results" auf "C#" einstellen)
Zitat von perlfred
Die letzte Möglichkeit besteht natürlich darin, eine neue Bibliothek mit der passenden C# Sprachversion zu erstellen und diese jetzt und in der Zukunft zu verwenden.
Das wäre auch eine Idee.
Ggf. arbeitest Du mit einem Shared Project als "gemeinsamen Nenner", der den konkreten Code enthält und dann mit preprocessor directives (gibt's für die .NET-Versionen eine ganze Menge) entscheidest, welcher Code verwendet wird.
Es funktioniert und Visual Studio hilft auch gut dabei, aber mehr als eine Übergangslösung sollte das nicht sein.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am .
Vielen Dank für deinen Link auf die Übersicht und Erklärung der Zusammenhänge und Entwicklung der .Net (Framework) Versionen.
Hast du (ihr) sehr verständlich erklärt! Hat mir geholfen!
Ich habe hier noch eine sehr anschauliche Grafik dazu gefunden.
Dass die Enum.TryParse<TEnum>(String, Boolean, TEnum)-Methode, wie in deinem Beispiel demonstriert, funktioniert, davon bin ich ausgegangen. Hier hast du aber den Ziel-Type (MyEnum) fest vorgegeben.
Ich such(t)e eine Enum.TryParse(Type, String, Object) Überladung, wie sie ab der C# Sprachversion 9.0 verfügbar ist, für ein .Net-Framework Projekt. Oder eine entsprechende Alternative, bei der man den EnumType als (TYPE) Variable angeben kann.
Ganz so fett ist meine WollMilchSau dann doch wieder nicht, ich möchte doch nur den Enum-Wert von einem Enum-Name eines beliebigen Enum-Typ's durch einen ValueConverter konvertieren.
Ich glaube du vermutest, dass ich etwas Anderes möchte, als es der Fall ist!
Prinzipiell funktioniert meine Anforderung:
Zitat
Ich möchte doch nur den Enum-Wert von einem Enum-Name eines beliebigen Enum-Typ's durch einen ValueConverter bestimmen.
Aus einem Enum-Name (name [String-Variable]) und einem Enum-Type (type [Type-Variable]) den Enum-Wert bestimmen:
Lösung von Palladin007:
if (Enum.TryParse(type, name, out var enumValue))
{
if (Enum.IsDefined(type, enumValue)) return (int)enumValue;
}
Ein Value Converter auf Basis der C# 9.0 Sprachversion funktioniert (wie erwartet) anstandslos.
Es ist also nicht die Frage ob es möglich ist, sondern ob eine "Entsprechung" für die Enum.TryParse(Type, String, Object) Überlagerung aus der C# 9.0 Sprachversion
mit "Bordmitteln" aus C# in der Spachversion C# 7.3 emuliert werden kann ???
Du könntest es dir auch auf umständliche Weise selber bauen.
Enum.GetValues() liefert dir alle Werte und Enum.GetName() liefert dir den Namen eines Wertes.
Mit den beiden Methoden müsstest Du dir den Wert anhand des Namens suchen können.
Aber schön ist das nicht und besser wäre, Du beseitigst den Grund, warum Du den Namen im ViewModel und den Wert in der View brauchst, das klingt nämlich etwas unsauber
Ich kenne es eher andersherum, dass man im ViewModel das Enum hat und in der View den Namen oder eine Lokalisierung dafür.
Und ich hab das Gefühl, ein und das selbe Problem verteilt sich gerade auf zwei Themen, zumindest weiß ich nicht mehr, wohin mit meiner Antwort.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palladin007 am .
Aber schön ist das nicht und besser wäre, Du beseitigst den Grund, warum Du den Namen im ViewModel und den Wert in der View brauchst
Dem ist natürlich nicht so. Das ist mehr dem EierlegendenWollMilchSau-Ansatz zu verdanken, da ich einen wirklich universellen ValueConverter erstellen will.
Sonst wäre es ja nur ein Einzeiler: (und Convert-Parameter bräuchte ich garnicht)
if (value != null && value.GetType().BaseType == typeof(Enum)) return (int)value; // Wenn ein Enum-Eintrag übergeben wurde kann sofort der Enum-Wert zurückgegeben werden.
Zitat
... das klingt nämlich etwas unsauber
Danke für die wohlwollende Umschreibung. ;-)
Zitat
Enum.GetValues() liefert dir alle Werte und Enum.GetName() liefert dir den Namen eines Wertes.
Mit den beiden Methoden müsstest Du dir den Wert anhand des Namens suchen können.
Aber schön ist das nicht ...
Aber ein großer Aufwand ist es auch nicht und in diesem Fall erst einmal eine gute Lösung!!!! (Danke für die Steilvorlage!)
Enum myEnum = Activator.CreateInstance(enumType) as Enum; // Enum-Instanz erstellen
Array WerteListe = Enum.GetValues(enumType); // Werte-Liste "holen"
foreach (var item in WerteListe) // Werte-Liste iterieren
if (Enum.GetName(enumType, item).Equals(name)) return (int)item; // Wenn der Enum-Name vorhanden ist, Enum-Wert zurückgeben
Die statischen Enum-Methoden sind goldwert!
An dieser Stelle auch noch ein Danke!!! an Alle die sich mit dem/meinem Problem beschäftigt haben!!!
Hier noch der gesamte ValuConverter (für C# 7.3):
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value.GetType().BaseType == typeof(Enum)) return (int)value; // Wenn ein Enum-Eintrag übergeben wurde kann sofort der Enum-Wert zurückgegeben werden.
string name = value?.ToString(); // Es wurde ein Enum-Name übergeben, diesen in String konvertieren
if (name is null || parameter is null || (Type)parameter is null) return -1; // Wurde zum Enum-Namen ein Enum-Type (im Parameter) übergeben? Nein -> Abbruch
Type enumType = (Type)parameter; // Ziel Enum-Type "holen"
if (!enumType.IsEnum) return -1; // Ist der Ziel-Type ein Enum-Type? Nein -> Abbruch
Enum myEnum = Activator.CreateInstance(enumType) as Enum; // Enum-Instanz erstellen
Array WerteListe = Enum.GetValues(enumType); // Werte-Liste "holen"
foreach (var item in WerteListe) // Werte-Liste iterieren
if (Enum.GetName(enumType, item).Equals(name)) return (int)item; // Wenn Enum-Name vorhanden ist, Enum-Wert zurückgeben
return -1; // Ansonsten -1 für: Es konnte kein Enum-Wert ermittelt werden.
}
PS:
Sorry, wie bekomme ich denn den Namen des Verfassers in das Zitat?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von perlfred am .
Die myEnum-Variable ist immer noch überflüssig und Du nutzt sie auch nicht.
Man sollte schon die letzte Version posten :o), (so etwas wird ja normalerweise vom VS schon "angemeckert"), aber ich hatte das automatisch korrigieren lassen (IntelliSense) und die sinnlose Anweisung:
_ = Activator.CreateInstance(enumType) as Enum; // Enum-Instanz erstellen
ist mir später nicht mehr aufgefallen.
Zitat von perlfred
if ( ... parameter is null || (Type)parameter is null)
Was dabei gedacht hatte ich mir schon ... If-Abfragen werden doch von links nach rechts abgearbeitet ... also erst geprüft ob parameter nicht gleich null ist und wenn dem so ist wird der parameter gecastet ... Ich hatte "übersehen", dass wenn der parameter nicht gecastet werden kann eine Exception geworfen wird und nicht , wovon ich ausgegangen bin, null wird. Was man doch im Alltag so alles verkehrt macht! Vielen Dank! für dein korrektes Beispiel!
Dein zweites Beispiel benutze ich so auch immer häufiger (erspart ein extra casten), es ist aber erst ab der C#-Version 9.0 implementiert.