Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Portal
  • |
  • Mitglieder
Beiträge von gfoidl
Thema: Was sind "Lokale statische Methoden"?
Am im Forum: Grundlagen von C#

Hallo RafaelVogt,

lokale Funktionen wurden mit C# 7 eingeführt. Durch praktischen Einsatz dieser Sprachversion wurde bemerkt, dass oft versehentliche lokale Variablen und Argumente in der lokalen Funktion verwendet wurde, worauf der Compiler dafür eine "Closure" erstellen musste. Da dies eben oft unbewusst gemacht wurde, entschied man sich mit C# 8 dem Abhilfe zu leisten und somit die "statischen lokalen Funktionen" einzuführen -- gekennzeichnet mit dem static Schlüsselwort bei der Deklaration der lokalen Funktion.

Wie bereits erwähnt und auch in der Fehlermeldung (indirekt) angegeben, muss dafür die Sprachversion 8.0 gesetzt werden. latest genügt hier nicht, es muss explizit 8.0 od. preview verwendet werden.

mfG Gü

Thema: Operator checked / checked Anweisungsblock
Am im Forum: Grundlagen von C#

Hallo RafaelVogt,

Zitat von RafaelVogt
Zitat von OlafSt
Das ist doch total simpel :D
Mit ...


short value20 = checked(Convert.ToInt16(Console.ReadLine()));

... machst du genau was?

Sorry... es ist echt verwirrend.

Hier macht das checked nicht viel Sinn, da Int16 der Typ des Schlüsselworts short ist. Da kannst du es weglassen.

Anders wäre es z.B. bei


short s0 = checked(Convert.ToUInt16(Console.ReadLine()));
da bei der Konvertierung* ein ushort zurückgegeben wird, das könnte bei der Zuweisung zu short überlaufen.
Od. beim Beispiel von oben mit der Zuweisung zu byte.

Generell ist auf (arithmetischem) Über-/Unterlauf zu achten, wenn verschiedene Wertebereiche im Spiel sind. Entweder wird explizit im Code geprüft ob es passt (z.B. per if) od. mit checked od. gar nicht, falls Überlauf akzeptabel ist.

* bitte unbedingt meinen obigen Hinweis zu den TryParse-Methoden beachten.

mfG Gü

Thema: Operator checked / checked Anweisungsblock
Am im Forum: Grundlagen von C#

Hallo RafaelVogt,

anstatt der checked Blöcke kann das auch "global" eingestellt werden.
Projekteigenschaften -> Build -> Advanced -> Checkbox "Check for arithmetic overflow/underflow"
Wobei ich jedoch die expliziten checked Blöcke und Anweisungen bevorzugen, da es eben sofort ersichtlich ist worum es geht.

Siehe zum Thema auch CA2233: Operations should not overflow

Da du gerade beim Lernen bist, noch ein Hinweis:

Zitat


short value20 = Convert.ToInt16(Console.ReadLine());
erzeugt einen Laufzeitfehler, falls Console.ReadLine keine Zahl (in Text-Form) liefert. Wenn du z.B. "abc" eingegeben hast anstatt einer erwarteten Zahl, so stürzt das Programm ab.
Abhilfe schafft entweder den Fehler mittels try catch zu fangen und behandeln od. noch besser


if (short.TryParse(Console.ReadLine(), out short value20))
{
    // Zahl konnte erfolgreich geparst werden
}
else
{
    // Zahl konnte nicht geparst werden
}
zu verwenden. Ob und wie auf das "nicht parsen" reagiert wird, hängt von der Anwendung ab.

mfG Gü

Thema: Beliebige doppelte Werte aus string filtern
Am im Forum: Grundlagen von C#

Hallo grubewol,

Zitat
ein doppelter wert
Meinst du damit explizit zwei hinteranderfolgende gleiche Zeichen?

Das könnte ganz naiv so gelöst werden:


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, int> counts = GetDoubledCharsCount("aabndmggmvndaasdfsdf");

            Console.WriteLine("Group\tCount");
            foreach (var kvp in counts.OrderByDescending(k => k.Value))
            {
                Console.WriteLine($"{kvp.Key}\t{kvp.Value}");
            }
        }

        private static Dictionary<string, int> GetDoubledCharsCount(string text)
        {
            var counts = new Dictionary<string, int>();

            for (int i = 1; i < text.Length; ++i)
            {
                if (text[i] == text[i - 1])
                {
                    string key = text.Substring(i - 1, 2);

                    if (counts.TryGetValue(key, out int count))
                    {
                        counts[key] = count + 1;
                    }
                    else
                    {
                        counts.Add(key, 1);
                    }
                }
            }

            return counts;
        }
    }
}

Die Aufgabe verallgemeinert auf beliebige Längen --> Longest common subsequence problem

mfG Gü

Thema: Wie realisiere ich die korrekte Fehlerbehandlung bei Tasks?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo tobi45f,

Zitat
Fehlt noch was?
Ich kenn deinen Code nicht, daher schwer genau zu sagen was es ist.
Bedenke jedoch dass der Code bis zum ersten await synchron ausgeführt wird und dann hängt es davon ab
  • der erwartete Task* schon fertig ist, d.h. es wird synchron weitergearbeitet
  • der erwartete Task* nicht fertig ist und im Hintergrund (asynchron) ausgeführt wird

Du kannst versuchsweise ziemlich am Beginn der Methode ein await Task.Yield() einbauen um async-Ausführung zu "erzwingen" und schauen ob es besser wird.

* der Einfachheithalber schreib ich nur Task, korrekter wäre "Awaitable" zu dem neben Task, ValueTask auch jedes selbst implementierte Awaitable gehört.

mfG Gü

Thema: Wie realisiere ich die korrekte Fehlerbehandlung bei Tasks?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo tobi45f,

Zitat
richtig? Vermutlich muss ich das Datenbank lesen+schreiben noch asyncen?!
Ja, alles async -- async ist viral :-)

mfG Gü

Thema: Wie realisiere ich die korrekte Fehlerbehandlung bei Tasks?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo tobi45f,

Zitat
Den GetAwaiter-Code verstehe ich leider gar nicht
Ganz grob: wenn der C#-Compiler ein await sieht, so verlangt er nach einem Typen der eine GetAwaiter-Methode (auch als Erweiterungsmethode) hat und diese Methode muss ein "awaitable" zurückgeben. Das ist z.B. bei Task der Fall.
Hier haben wir einen Process und wollen diesen asynchron erwarten, da der C#-Compiler jedoch nicht weiß was und wie er dies tun soll, helfen wir ihm mit dieser Erweiterungsmethode.
Zitat
dass ich das nicht so umsetzen kann, da ich den Konsoleninhalt auslese, um das Simulationsergebnis zu interpretieren.
Ja so, kann die Erweiterungsmethode nicht 1:1 verwendet werden, da nur der ExitCode zurückgegeben wird und nicht die Ausgabe aus stdout. Dazu muss diese aus meinem vorigen Post angepasst werden:


using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string stdout = await "cmd.exe".ReadStdOut();
            Console.WriteLine($"stdout: {stdout}");
        }
    }

    internal static class ProcessExtensions
    {
        public static Process ReadStdOut(this string command)
        {
            var startInfo = new ProcessStartInfo(command);
            startInfo.RedirectStandardOutput = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;

            var process = new Process();
            process.StartInfo = startInfo;
            process.Start();

            return process;
        }

        public static TaskAwaiter<string> GetAwaiter(this Process process)
        {
            var tcs = new TaskCompletionSource<string>();
            process.EnableRaisingEvents = true;

            process.Exited += (s, e) => tcs.TrySetResult(process.StandardOutput.ReadToEnd());
            if (process.HasExited) tcs.TrySetResult(process.StandardOutput.ReadToEnd());

            tcs.Task.ContinueWith(
                (_, p) => (p as Process).Dispose(),
                process,
                TaskContinuationOptions.ExecuteSynchronously);

            return tcs.Task.GetAwaiter();
        }
    }
}
(als Beispiel, kannst und sollst du natürlich anpassen wie es dir passt).
Zitat
aus dem await Task<TradeSolutions>.Factory.StartNew(() rausgezogen, damit nicht der API-Kram nicht den Task beendet.
Das hatten wir deshalb gemacht, damit ein Fehler im "inneren Task" auch beobachtet werden kann. Durch Task.Factory.StartNew war das nicht der Fall, daher ist die App gecrasht.
Wenn hier nur der Process per await erwartet wird, so gibt es nur einen Task und hat dieser einen Fehler, so wird dieser auch gefangen. Kein Problem.
Allerdings ist in obiger (meiner) Umsetzung vom GetAwaiter für den Process nichts eingebaut um den Task in eine Fehler-Zustand zu setzen (ginge via tcs.TrySetException, das kannst du ja selbst machen -- siehe angehängtes Beispiel)


mfG Gü

Thema: Wie realisiere ich die korrekte Fehlerbehandlung bei Tasks?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo tobi45f,

Zitat
ein Berechnungsalgorithmus bestehende aus einer Schleife mit: Datenbanken lesen, Werte Interpretieren, ...
Somit kannst du beim DB-Lesen asynchron vorgehen.

Auch den externen Prozess kannst du asynchron "erwarten". Dazu brauchst du nur eine Erweiterungsmethode, siehe Bsp.:


using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            int exitCode = await Process.Start("cmd.exe");
            Console.WriteLine($"exit code: {exitCode}");
        }
    }

    internal static class ProcessExtensions
    {
        public static TaskAwaiter<int> GetAwaiter(this Process process)
        {
            var tcs = new TaskCompletionSource<int>();
            process.EnableRaisingEvents = true;

            process.Exited += (s, e) => tcs.TrySetResult(process.ExitCode);
            if (process.HasExited) tcs.TrySetResult(process.ExitCode);

            tcs.Task.ContinueWith(
                (_, p) => (p as Process).Dispose(),
                process,
                TaskContinuationOptions.ExecuteSynchronously);

            return tcs.Task.GetAwaiter();
        }
    }
}
Zitat
etzt ist der Code zwar nicht mehr so Chronologisch übersichtlich im Ablauf,
Das lässt sich aber wieder übersichtlicher gestalten, ev. durch gezieltes Refactoring.
Leserlicher Code ist sehr wichtig und auch dass sinnvolle Bezeichner verwendet werden.

mfG Gü

Thema: Wie realisiere ich die korrekte Fehlerbehandlung bei Tasks?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo tobi45f,

Zitat
wann ich über einen neuen Task die Methode aufrufe und wann ich nur die asynchrone Methode mit await aufrufe? Wonach kann man sich da richten, wie/wann man was anwendet?
Grundsätzlich muss bei Vorgängen unterschieden werden ob diese CPU- od. IO-lastig sind.
CPU-lastig sind z.B. Berechnungen die direkt im RAM erfolgen können.
IO ist Datei-, Netzwerk-, Datenbank-Zugriff usw.

Task ist dabei eine .NET-Abstraktion von "Arbeitsaufträgen", welche nicht zwingend mit Thread gleichzusetzen sind.

Der Großteil von IO kann in .NET asynchron erfolgen und wird (mittlerweile) durch Tasks abstrahiert. Z.B.


public async Task<int> DownloadAsync(string url)
{
    // Code vorher
    string result = await _httpClient.GetStringAsync(url);
    // Code nachher
    return result.Length;
}
Was passiert hier?
  1. die Methode DownloadAsync wird synchron aufgerufen
  2. Code vorher wird synchron ausgeführt
  3. Code GetStringAsync wird im HttpClient synchron ausgeführt, bis intern ein IO-async-Vorgang gestartet wird (der HTTP-Request), dann wird von GetStringAsync ein Task<string> zurückgegeben
  4. durch das await (und dem vom C#-Compiler umgeschrieben Code) wird das Ergebnis vom Task<string> erwartet (nota bene: ich schreibe bewusst nicht "gewartet", denn diese würde blockierendes Warten implizieren und das gilt es nämlich zu vermeiden, daher "erwartet")
  5. DownloadAsync gibt dem Aufrufer der Methode ein Task<int>-Objekt zurück
  6. sobald der Http-Request fertig ist und der Http-Response zurückkommt, so wird vom IO-ThreadPool ein sogenannter "IO-Abschlussthread" genommen, welcher die weitere Verarbeitung durchfüht --> jetzt wird mit dem Code nach dem await (der Continuation) forgefahren
  7. Code nachher wird wieder syncrhon ausgeführt (aber möglicherweise in einem anderen Therad als Code vorher*)
  8. es wird das Ergebnis return result.Length zurückgeben, dabei der Task<int> "vervollständigt" und dessen Result-Eigenschaft hat nun den Wert der per return zurückgegeben wird od. die Exception-Eigenschaft hat einen Fehler im Fehlerfall

* dieses Verhalten kann auch mit ConfigureAwait beeinflusst werden -- behandle ich hier nicht weiter

Der Vorteil von async-IO ist, dass keine CPU-Threads v.a. aus dem ThreadPool verbraten werden od. keine solche Threads unnötig durch Warten blockiert werden -- dies ist v.a. in Server-Umgebungen für die Skalierbarkeit entscheidend.

Daher sollte IO so gut es möglich ist asynchron erfolgen.
In ASP.NET Core sind standardmäßig z.B. keine synchronen (= blockierenden) IO-Vorgänge gestattet (zu Recht ;-)).
Aber auch für Dektop-Anwendungen ist dies vorteilhaft, da das gesamte System einfach besser läuft, wenn nicht (wertvolle) CPU-Thread wartend ihre Zeit verbringen.

Bei reiner CPU-Last (Berechnungen, etc.) ist zwischen Server- und Desktop-Anwendungen zu unterscheiden.

Server-Anwendungen können die Berechnungen durchwegs in ihrem Request-Thread durchführen (nicht jedoch IO-Vorgänge, siehe vorhin). Da der Request-Thread bereits dem ThreadPool entnommen wurde, macht es kaum Sinn nochmals in einen anderen ThreadPool-Thread die Arbeit zu delegieren.

Desktop-Anwendungen müssen wegen [FAQ] Warum blockiert mein GUI? CPU-lastige Aufgaben in einem nicht GUI-Thread ausführen, d.h. die Arbeit in einem anderen Thread auslagern.
Wenn es sich um "kurze" Dauern für die CPU-Aufgabe handelt ist der ThreadPool zu bevorzugen, da das Erstellen und Zerstören eines Threads recht aufwändig ist (daher gibt es ja den ThreadPool). Dauer die CPU-Aufgabe zu lange, so wird zwar der ThreadPool durch seine Thread-Injektion-Heuristik mehr Thread dem Pool hinzufügen, aber optimal arbeitet der ThreadPool nur für kurze Aufgaben.
Kurz und lang sind dabei relativ, aber so als groben Richtwert kann man 1s verwenden.
Der Einfachheithalber würde ich aber für Berechnungen auch den ThreadPool verwenden, da der Code einfach kürzer und eleganter wird.

Also um die oben zitierte Frage auch kurz zu beantworten:
  • für IO "immer" per async / await ohne Task.Factory.StartNew bzw. ohne Task.Run
  • für CPU bei Desktop mittels Task.Run (od. entsprechende ThreadPool.QueueUserWorkItem-Äquivalente)
Zitat
die Routine, die dem Post vorweg geht. Diese hat eine lange Berechnungsdauer (einige Minuten)
OK, das wusste ich vorher noch nicht. Es geht dabei um GetMarketArea? Ist das wirklich eine Berechnung im Sinne von CPU-Last od. doch eher Datenbank-Zugriffe, usw. also IO?

Nehmen wir an es ist CPU, dann ist auch das möglich. Beispiel:


#define CPU_BOUND

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        private readonly Worker _worker = new Worker();
        private CancellationTokenSource _cancellationTokenSource;

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void StartButton_Click(object sender, RoutedEventArgs e)
        {
            if (_cancellationTokenSource == null)
                _cancellationTokenSource = new CancellationTokenSource();

            startButton.IsEnabled = false;
            stopButton.IsEnabled = true;

            try
            {
                statusTextBox.Text = "MarketArea...";
#if CPU_BOUND
                MarketArea marketArea = await Task.Run(() => _worker.ComputeMarketArea(), _cancellationTokenSource.Token);
                if (_cancellationTokenSource?.IsCancellationRequested ?? true) return;
#else
                MarketArea marketArea = await _worker.GetMarektAreaAsync(_cancellationTokenSource.Token);
#endif
                statusTextBox.Text = "MarketArea OK";

                if (await _worker.PostObjectAsync("bla bla", marketArea, _cancellationTokenSource.Token))
                {
                    statusTextBox.Text = "OK";
                }
                else
                {
                    // Nicht Erfolg behandeln (od. PostObjectAsync gibt einen string / Exception zurück, dazu müsste ich
                    // aber das Problem / Aufgabe besser verstehen)
                }
            }
            catch (OperationCanceledException)
            {
                statusTextBox.Text = "Übermittlung wurde abgebrochen";
            }
            catch (Exception ex)
            {
                statusTextBox.Text = ex.Message;
            }
            finally
            {
                this.Reset();
            }
        }

        private void StopButton_Click(object sender, RoutedEventArgs e) => this.Reset();

        private void Reset()
        {
            _cancellationTokenSource?.Dispose();
            _cancellationTokenSource = null;

            this.ResetButtons();
            statusTextBox.Text = string.Empty;
        }

        private void ResetButtons()
        {
            startButton.IsEnabled = true;
            stopButton.IsEnabled = false;
        }
    }

    public class MarketArea { }

    public class Worker
    {
        private readonly HttpClient _httpClient = new HttpClient();

        public async Task<MarketArea> GetMarektAreaAsync(CancellationToken cancellationToken = default)
        {
            // Lange Operation simulieren
            await Task.Delay(500, cancellationToken).ConfigureAwait(false);

            return new MarketArea();
        }

        public MarketArea ComputeMarketArea()
        {
            // Lange Berechnung simulieren
            for (int i = 0; i < int.MaxValue; ++i)
            {
            }

            return new MarketArea();
        }

        public async Task<bool> PostObjectAsync<T>(string path, T data, CancellationToken cancellationToken = default)
        {
            // HttpClient-header bereits gesetzt

            try
            {
                // Wenn hier oft der Server nicht verfügbar ist, so kann ein Retry, etc. eingebaut werden
                // Siehe z.B. Polly (einfach googlen im Kontext)
                HttpResponseMessage response = await _httpClient.PostAsJsonAsync(path, data, cancellationToken).ConfigureAwait(false);

                if ((int)response.StatusCode == 450)
                {
                    // Logging -- kein Grund eine Exception zu schmeißen, wenn diese ein paar Zeilen später im catch
                    // gefangen wird, das kann dann gleich hier erfolgen
                    string msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    // msg verarbeiten

                    return false;
                }

                return response.IsSuccessStatusCode;
            }
            catch (Exception ex)
            {
                // Logging, etc. von ex

                // Es geht hier nur um den Fehlerfall, daher den Fehler erneut schmeißen
                // Nicht HTTP-200 werden hier nicht behandelt
                throw;
            }
        }
    }
}
Es gibt so viele Möglichkeiten das umzusetzen. Z.B. in ComputeMarketArea könnte auch der CancellationToken übergeben werden um dort kooperativ abzubrechen.

Aber letztlich reicht ein await Task.Run aus um die CPU-Aufgabe zum ThreadPool zu verlagern, ohne dabei irgendwelche Fehler nicht behandeln zu können. Probiers einmal aus.
Zitat
Bzgl. des httpClient meinst du das hier, zwar nicht HttpClientFactory aber inhaltlich sehr ähnlich?
Genau :-)

mfG Gü

Thema: Wie realisiere ich die korrekte Fehlerbehandlung bei Tasks?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo tobi45f,

ersetzte


await Task.Factory.StartNew(() => berechnung.PostTAsync(pathFidList, berechnung.GetMarketArea()), token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
durch


await berechnung.PostTAsync(pathFidList, berechnung.GetMarketArea()), token);
(den CancellationToken sollte als Argument an PostTAsync übergeben werden können)

Dann sollte das Fehlerhandling korrekt klappen.
Bei deinem Code mit Task.Factory.StartNew wird nicht nur unnötig ein Thread gestartet, sondern auch await "erwartet" den Task der gerade gestartet wurde und nicht jenen der vom PostAsJsonAsync stammt. Somit resultiert das in einen "unbehandleten Task-Fehler" und wegen dem finally wird dort fortgesetzt.

Ohne das Task.Factory.StartNew wird bei await das "Ergebnis" von PostAsJsonAsync erwartet und das kann ein Resultat (hier das bool) od. eben ein Fehler sein.

Da PostAsJsonAsync für sich schon IO-asynchron ist, macht es -- wie vorhin schon kurz erwähnt -- keinen Sinn extra einen Thread zu starten (wegen LongRunning wird kein ThreadPool-Thread verwendet).

Weiters solltest du den HttpClient (für die gleiche Url) nicht jedesmal neu erstellen, da selbst beim Dispose die Socket-Verbindungen nicht ganz geschlossen werden und es somit zu einer "Ausschöpfung" kommen kann. Google mal danach, dieser Punkt wird ausführlich behandelt, daher gebe ich hier nicht alles wieder.
Kurz: verwende am besten die HttpClientFactory, denn diese verwendet einen Pool und erledigt das alles für dich.

mfG Gü

Thema: Gibt es ein Enum mit Double Werten?
Am im Forum: Grundlagen von C#

Hallo dila71,

Zitat
Ich dachte bisher Double Enums gäbe es nicht.
Google sagt auch nur, geht nicht.
Nach was kann/muss ich googeln, um mich darüber zu informieren.
Naja, die offizielle Doku?
Zitat von enum (C# Reference)
Every enumeration type has an underlying type, which can be any integral numeric type.
double ist kein "integral numeric type" (siehe dazu auch [FAQ] Double und Float: Fehler beim Vergleich und Rundungsfehler).

mfG Gü

Thema: .NET-Anwendung mit dll unter Raspian mit Mono
Am im Forum: Cross Platform Entwicklung - Mobile und IoT

Hallo CrocodileDundee,

Zitat
.NET Core 3 wird auch direkt auf dem Raspberry laufen.
.NET Core 2.1 und neuer laufen auf dem rasperrybi (3) problemlos.
Zitat
eine selbst geschriebene dll
Ergänzend zu Abts Hinweis "funktioniert nicht": es ist eh eine managed DLL? Bei einer nativen DLL muss diese für arm (rpi) kompiliert worden sein, sonst kann es ohnehin nicht gehen.

mfG Gü

Thema: Wie realisiert man Kommunikation über Pipes?
Am im Forum: Rund um die Programmierung

Hallo FrankenDerStein,

ich will Abts Antwort ergänzen / untermauern.

(Named) Pipes sind genauso wie TCP "low level" und i.d.R. ist es nicht nötig so tief (im Stack) selbst zu programmieren. Wesentlich effizienter und komfortabler ist es Frameworks zu verwenden, welche die low level Kommunikation wegabstrahieren. gRPC ist momentan das Maß der Dinge (aktuell auch zurecht).

Pipes, TCP, etc. würde ich nur dass verwenden wenn es nicht anders geht.

mfG Gü

Thema: Null-Reference Exception nicht nachvollziehbar
Am im Forum: Grundlagen von C#

Hallo CoderboyPB,

Zitat
(Wo nur Text gesendet wird) dann 'Pseudo Async' machen
Task.FromResult(TResult) Method hilft dir hierbei.


public Task<string> FooAsync()
{
    string result = // ...

    return Task.FromResult(result);
}


mfG Gü

Thema: Anmedlung an IBM AS/400
Am im Forum: Netzwerktechnologien

Wegen [Hinweis] Wie poste ich richtig? Punkt 2.2 ==> geschlossen

Crosspost

Thema: Anmedlung an IBM AS/400
Am im Forum: Netzwerktechnologien

Hallo Medicus,

Zitat
Gibt es dafür auch eine Verbindung über ADO.NET?
Google-Suche nach ado.net as400 ;-)
Zitat
Sollte ich den Code für die Anmeldung im 'program.cs' oder Hauptform schreiben?
Weder noch, siehe dazu [Artikel] Drei-Schichten-Architektur

mfG Gü

Thema: ASP.NET Core 2 ConnectionString aus appsettings.json lesen und in Registrierung übergeben?
Am im Forum: Web-Technologien

Hallo,

Zitat
ja, es gibt sehr wohl eine Middleware/Pipeline, wie die Konfiguration verarbeitet wird.
wenn wir unter Middleware die ASP.NET Core Middleware verstehen -- davon ist im Kontext hier auszugehen -- so hat die Konfiguration damit nichts zu tun.
Zitat
Configuration Builder.
Jede gefundene, gematche Konfiguration durchläuft dann eine Pipe.
Mag sein, aber nicht in ASP.NET Core, wie im Code zu sehen ist ;-)
Im ConfigurationBuilder ist es im Wesentlichen eine List<IConfigurationSource> zu der Einträge hinzugefügt werden um dann im Build (gem. Erbauer (Entwurfsmuster), daher auch der Suffix im Namen) die ConfigurationRoot zu erstellen. Dabei spielt eben die Liste eine entscheidende Rolle, da so über die Reihenfolge des Hinzufügens die resultierenden Config-Werte ermittelt werden (kurz: das später Hinzugefügte überschreibt das vorherige). Mit Pipe hat das nichts zu tun, da es eine (triviale) Iteration vom Listenende zum Listenanfang ist.

mfG Gü

Thema: ASP.NET Core 2 ConnectionString aus appsettings.json lesen und in Registrierung übergeben?
Am im Forum: Web-Technologien

Hallo,

Zitat
Konfigurationsmiddleware
Die gibt es nicht (zumindest per Standard ASP.NET Core). ConfigurationBuilder hingegeben schon.

mfG Gü

Thema: DateTime Verrechnen und Zeitspanne auf Tage aufrunden
Am im Forum: Grundlagen von C#

Hallo deluxe13,

willst du so etwas?


using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace Tests
{
    [TestFixture]
    public class Tests
    {
        [Test, TestCaseSource(nameof(Correct_rounding_TestCases))]
        public TimeSpan Correct_rounding(TimeSpan input)
        {
            return input.RoundToWholeDays();
        }

        private static IEnumerable<TestCaseData> Correct_rounding_TestCases()
        {
            yield return new TestCaseData(new TimeSpan(1, 0, 0, 0)).Returns(new TimeSpan(1, 0, 0, 0));
            yield return new TestCaseData(new TimeSpan(1, 0, 0, 1)).Returns(new TimeSpan(2, 0, 0, 0));
            yield return new TestCaseData(new TimeSpan(1, 0, 1, 0)).Returns(new TimeSpan(2, 0, 0, 0));
            yield return new TestCaseData(new TimeSpan(1, 1, 0, 0)).Returns(new TimeSpan(2, 0, 0, 0));
        }
    }

    public static class Extensions
    {
        public static TimeSpan RoundToWholeDays(this TimeSpan timeSpan)
        {
            int days = timeSpan.Days;

            return timeSpan.TotalDays == days
                ? timeSpan
                : new TimeSpan(days + 1, 0, 0, 0);
        }
    }
}

Hier hab TotalDays verwendet.


mfG Gü

Thema: Wie kann ich bei einem OPC/UA auf eine offene Verbindung prüfen?
Am im Forum: Netzwerktechnologien

Hallo RoyalRoy,

angenommen es gibt es Möglichkeit die Verbindung zu prüfen.

Folgendes Szenario:

  1. Verbindung wird hergestellt
  2. Verbindung wird überprüft
  3. Daten werden auf den Server geschrieben

Das willst du haben, damit ein Abbrechen nach dem Herstellen der Verbindung und vor dem Schreiben erkannt wird. Korrekt?

Stell dir jetzt vor, dass die Verbindung nach dem Prüfen abgebrochen wird. Was hast du dann gewonnen?

Kurz: du baust mehr Aufwand in deine Anwendung ein ohne dabei einen Netto-Gewinn zu haben.

Ich würde im Fehlerfall einfach den Fehler / Exception handhaben (Retry, Loggen, etc. wie es halt angebracht ist) und im Regelfall davon ausgehen dass es eh klappt.

mfG Gü

Thema: DateTime Verrechnen und Zeitspanne auf Tage aufrunden
Am im Forum: Grundlagen von C#

Hallo deluxe13,

schau dir TimeSpan.TotalMinutes dazu an. Den Rest schaffst du dann schon ;-)

mfG Gü

Thema: MSSQL & LINQ to SQL - Änderungen in der DB werden erst nach Neustart des Programms angezeigt
Am im Forum: Datentechnologien

Hallo deluxe13,

Zitat
Was mache ich falsch?
Dass du dir hier eine Antwort erhoffst, ohne uns etwas über deinen Code zu sagen / zu zeigen ;-)

Wir haben keine Ahnung wie deine Anwendung aufgebaut ist und was sie machen soll, daher können wir entweder nur pauschale Antworten geben, die werden dir aber nicht viel weiterhelfen, od. raten und auch das wird dir nicht weiterhelfen.

Also stell dir vor du liest diese Frage ohne zu wissen worum es geht. Was würdest du antworten?

Als pauschale Antwort kann ich hier nur schreiben, dass es i.d.R. keine gute Idee ist beim Programmstart den DB-Kontext zu laden und dann für die dauer der Anwendung offen zu halten, siehe Unit of Work-Pattern.

mfG Gü

Thema: Daten aus DB in ein XML einlesen
Am im Forum: Datentechnologien

Hallo Anna85,

geht es um das selbe Problem wie bisher od. ist es ein Neues?

mfG Gü

Thema: Hinweis: C# 8.0 nullable reference types -- Achtung vor falscher Sicherheit
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo BhaaL,

Zitat
Kommt die NRE beim Zugriff auf person.Name.Length oder bereits beim Konstruktor-Aufruf new Person(null)?
Das läuft ab wie bisher, also beim Zugriff auf Length, da Name null ist.
Zitat
Wies scheint hatte ich hier ein altes Proposal ...
Das soll in Zukunft auch kommen. Allerdings weiß ich nicht wie "Zukunft" hier definiert ist.

mfG Gü

Thema: Hinweis: C# 8.0 nullable reference types -- Achtung vor falscher Sicherheit
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo p!lle,

konkret geht es um die Analogie zu Nullable types (wie z.B. int?), daher passt der Name mit Nullable Reference Types schon.

Mit #nullable enable werden die Nullable Reference Types als optionale Erweiterung der C#-Sprache aktiviert.

Optional ist das Ganze deshalb, damit für betehenden Code keine breaking-change passiert bzw. bestehender Code auch weiterhin kompiliert ohne Warnungen / Fehler zu generieren.

mfG Gü

Thema: Hinweis: C# 8.0 nullable reference types -- Achtung vor falscher Sicherheit
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Hallo zusammen,

wie vllt. bekannt ist wird mit C# 8.0 der Kampf dem Billion Dollar MistakeTony Hoare) angesagt. Siehe dazu Design with nullable reference types.

Allerdings darf man sich v.a. bei öffentlichen (public) APIs nicht in falscher Sicherheit wiegen.

Diese Gefahr / Tücke will ich mit nachfolgenden simplen Beispiel demonstrieren.


#nullable enable

    public class Person
    {
        public string Name { get; }

        public Person(string name) => this.Name = name;
    }
}
Für die öffentliche Klasse Person wurde das "nullable feature" explizit aktiviert*, somit könnte man meinen, dass name eben nicht null sein darf.
Das ist für diese Klasse auch korrekt**, andernfalls hätte der Parameter als string? name deklariert werden müssen um null zu erlauben.

Wenn nun ein Benutzer unserer Klasse Person Nullable nicht aktiviert hat, wie im folgenden Code


    class Program
    {
        static void Main(string[] args)
        {
            var person = new Person(null);
            Console.WriteLine(person.Name.Length);
        }
    }
, so kompiliert das Programm ohne Fehler / Warnung, zur Laufzeit gibt es aber eine NullReferenceException, da nirgends validiert wurde ob das Konstruktor-Argument name ungleich Null ist.

Wäre für obigen Code Nullable auch aktiviert worden, so hätte der C#-Compiler eine Warnung / Fehler ausgegeben.
Beim Erstellen eines "public apis" können wir aber nicht davon ausgehen, dass jeder Benutzer Nullable aktiviert hat und somit können wir in die vorhin demonstrierte Falle tappen.

Somit will ich mit diesem Beitrag den Hinweis ausprechen:
Auch mit C# 8.0 Nullable müssen public api auf null validiert werden!.

D.h. eingangs erwähnte Person-Klasse ist auch in Zukunft korrekt zu schreiben:


#nullable enable

    public class Person
    {
        public string Name { get; }

        public Person(string? name) => this.Name = name ?? throw new ArgumentNullException(nameof(name));
    }

Hier wurde als Typ string? angegeben, damit verdeutlicht wird dass Verwender dieser Klasse auch null dürfen.

Wäre als Typ string angegeben und der Verwender dieser Klasse hat Nullable sowie TreatWarningsAsErrors aktiviert, so ließe sich der Code mit null gar nicht kompilieren. Dies kann u.U. ein Hindernis sein. Daher bevorzuge ich (momentan***) die Variante mit string?.


* es ist auch eine Aktivierung via Projekteinstellungen möglich, ich empfehle aber die Aktivierung per Compiler-Direktive vorzunehmen, da so beim Lesen vom Code sofort ersichtlich dass "opt-in" für Nullable durchgeführt wurde.

** korrekt im Rahmen der vom Compiler durchgeführten statischen Codeanalyse, die entweder als Warnung od. bei <TreatWarningsAsErrors>true</TreatWarningsAsErrors> (in der csproj) als Fehler ausgegeben wird

*** Nullable ist noch relativ neu, daher kann es sein dass sich meine Präferenz dafür ändert und string (also ohne das Zulassen von null) die bessere Wahl ist

mfG Gü

Thema: Stimmt es, dass Umgebungsvariablen im ASP.NET Core sich nur auf die Anwendung selbst beziehen?
Am im Forum: Web-Technologien

Hallo CoderboyPB,

es sind "normale" Umgebungsvariablen. Die Gültigkeit ist entweder auf Betriebssystemebene, od. die aktuelle Shell / Programm und deren Sub-Shells / aufgerufenen Programme.

ASP.NET Core hat hier keine Sonderstellung in Bezug auf die Umgebungsvariablen.

mfG Gü

Thema: Projekt-Ideen für WinForms- und Konsolenprojekte?
Am im Forum: Smalltalk

Hallo MinisBett,

Zitat
auf 8/10 Punkten. Mit Asynchronen Dingen, "await", Tasks, usw. habe ich noch nichts gemacht,
Dann kann 8/10 nicht stimmen ;-)
Ich würde dann am besten ein Projekt suchen / erstellen um mit den asynchronen Dingen rumzuspielen. Genauso würde ich beim Framework auf .NET Core setzen, wenn du dort noch keine Erfahrung hast.

mfG Gü

Thema: Xml erstellen und Daten einlesen
Am im Forum: Datentechnologien

Hallo Anna85,

wegen [Hinweis] Wie poste ich richtig? Punkt 2.2 ==> geschlossen

Crosspost.

mfG Gü

Thema: dynamische If-Abfrage
Am im Forum: Rund um die Programmierung

Hallo Xargus,

im K-Array stehen nur boolsche Werte?
Dann nimm ein Bitfeld / Bitvektor und setzt entsprechend Index das jeweilige Bit. So kannst du einfach mit == 0 od. != 0 prüfen.

Siehe dazu auch [Artikel] Bitoperationen in C#

mfG Gü