Entwicklung & Code
Datenparallele Typen in C++26: Bedingte Ausführung von Operationen
Leider habe ich in meinem letzten Beitrag „Datenparallele Typen in C++26: ein Beispiel aus der Praxis“ vergessen, eine Funktion der neuen Bibliothek vorzustellen. Das hole ich in diesem Artikel nach.
Rainer Grimm ist seit vielen Jahren als Softwarearchitekt, Team- und Schulungsleiter tätig. Er schreibt gerne Artikel zu den Programmiersprachen C++, Python und Haskell, spricht aber auch gerne und häufig auf Fachkonferenzen. Auf seinem Blog Modernes C++ beschäftigt er sich intensiv mit seiner Leidenschaft C++.
Where-Ausdruck
Das neue Schlüsselwort where
erzeugt einen sogenannten Where-Ausdruck. Damit lassen sich die Elemente eines SIMD-Vektors bedingt ansprechen.
Folgendes Beispiel bringt dieses Verhalten auf den Punkt:
// where.cpp
#include
#include
#include
namespace stdx = std::experimental;
void println(std::string_view name, auto const& a)
{
std::cout << name << ": ";
for (std::size_t i{}; i != std::size(a); ++i)
std::cout << a[i] << ' ';
std::cout << '\n';
}
template
stdx::simd my_abs(stdx::simd x)
{
where(x < 0, x) = -x; // Set elements where x is negative to their absolute value
return x;
}
int main()
{
const stdx::native_simd a = 1;
println("a", a);
const stdx::native_simd b([](int i) { return i - 2; });
println("b", b);
const auto c = a + b;
println("c", c);
const auto d = my_abs(c);
println("d", d);
}
In der Funktion my_abs
kommt die where
-Funktion zum Einsatz: where(x < 0, x) = -x;
bewirkt, dass alle Elemente des SIMD-Vektors, die kleiner als Null sind, auf ihren absoluten Wert gesetzt werden.
Der Screenshot zeigt die Ausgabe des Beispielcodes.
In diesem Fall kommen SSE2-Befehle zum Einsatz. Der SIMD-Vektor ist 128 Bit groß.
Die where
-Expression kann mit einem bool
-Ausdruck oder einer simd_mask
parametrisiert werden.
Obiges Codebeispiel lässt sich auch mit einer simd_mask
implementieren. Folgender Code zeigt die Umsetzung:
// whereMask.cpp
#include
#include
#include
namespace stdx = std::experimental;
void println(std::string_view name, auto const& a)
{
std::cout << std::boolalpha << name << ": ";
for (std::size_t i{}; i != std::size(a); ++i)
std::cout << a[i] << ' ';
std::cout << '\n';
}
int main()
{
const stdx::native_simd a = 1;
println("a", a);
const stdx::native_simd b([](int i) { return i - 2; });
println("b", b);
const auto c = a + b;
println("c", c);
const stdx::native_simd_mask x = c < 0;
println("x", x);
auto d = c;
where(x, d) *= -1;
println("d", d);
}
Beginnen möchte ich meine Erläuterung mit den letzten fünf Zeilen der Main-Funktion. Zuerst erzeuge ich die simd_mask
x, indem das Prädikat c < 0
auf jedes Element des SIMD-Vektors c angewendet wird.
Die Maske x hat die gleiche Länge wie der SIMD- Vektor, besitzt aber nur Wahrheitswerte. Damit diese Wahrheitswerte als true
oder false
und nicht als 1 oder 0 dargestellt werden, habe ich der Funktion println
den Streammanipulator std::boolalpha
hinzugefügt.
Zusätzlich muss ich den SIMD-Vektor d mit c initialisieren, da c konstant ist. Nun lässt sich die Expression where(x, d) *= -1;
auf d anwenden. Dabei wird jedes Element des SIMD-Vektors negiert, wenn die Maske den Wert true
besitzt.
Der Screenshot zeigt die Ausgabe des Codes mit simd_mask.
Der Datentyp simd_mask
ist dem Datentyp simd
sehr ähnlich. Der wesentliche Unterschied besteht darin, dass simd
alle Standard-Ganzzahltypen, Zeichentypen und die Typen float und double annehmen kann. Im Gegensatz dazu unterstützt simd_mask
nur Wahrheitswerte.
Die Definition von simd_mask
sieht folgendermaßen aus:
template
class basic_simd_mask
Der Abi-Tag bestimmt die Anzahl der Elemente und deren Speicherplatz. Zur Vollständigkeit sind hier noch einmal die ABI-Tags:
scalar
: Speichern eines einzelnen Elementsfixed_size
: Speichern einer bestimmten Anzahl von Elementencompatible
: gewährleistet ABI-Kompatibilitätnative
: am effizientestenmax_fixed_size
: maximale Anzahl von Elementen, die von fixed_size garantiert unterstützt werden
Entsprechend zu simd
besitzt simd_mask
auch zwei Aliase:
template< size_t Bytes, int N >
using fixed_size_simd_mask = simd_mask>
template< size_t Bytes >
using native_simd_mask = simd_mask>
Wie geht‘s weiter?
In meinem vorerst letzten Artikel über data-parallel types möchte ich auf die besonderen Funktionen dafür eingehen.
(rme)
Entwicklung & Code
Event-Driven, Teil 3: Wie man über Ereignisse spricht
Im zweiten Teil dieser Serie haben wir gesehen, welche Bausteine eine Event-getriebene Architektur ausmachen: Commands, Events, Projections und Event Streams. Dabei spielen Events eine zentrale Rolle – sie sind die Sprache, in der das System erzählt, was passiert ist.
Golo Roden ist Gründer und CTO von the native web GmbH. Er beschäftigt sich mit der Konzeption und Entwicklung von Web- und Cloud-Anwendungen sowie -APIs, mit einem Schwerpunkt auf Event-getriebenen und Service-basierten verteilten Architekturen. Sein Leitsatz lautet, dass Softwareentwicklung kein Selbstzweck ist, sondern immer einer zugrundeliegenden Fachlichkeit folgen muss.
Doch was genau ist ein Event eigentlich? Wie benennt man es? Und wie stellt man sicher, dass es fachlich sinnvoll, verständlich und nützlich ist?
Events sind fachliche Tatsachen
Ein Event beschreibt etwas, das in der Vergangenheit liegt und fachlich bedeutsam ist. Es geht nicht um technische Details („Request empfangen“ oder „Datenbankverbindung geöffnet“), sondern um Fakten, die für die Domäne relevant sind: „Buch wurde ausgeliehen“, „Mahngebühr wurde berechnet“ oder „Adresse wurde umgemeldet“.
Diese Ereignisse sind die kleinste gemeinsame Sprache zwischen Fachlichkeit und Technik. Sie dokumentieren Entscheidungen, Abläufe und Zustandsänderungen – nicht als Nachgedanke, sondern als primäres Modell.
Die Sprache der Fachlichkeit
Events sollten so formuliert sein, dass sie auch (beziehungsweise vor allem) von Fachleuten verstanden werden und – im Idealfall – wurden sie sogar gemeinsam mit ihnen entwickelt. Es geht darum, eine gemeinsame Sprache zu finden, die sowohl die Business-Perspektive als auch die technische Umsetzung trägt.
Ein gutes Event erfüllt folgende Kriterien:
- Es beschreibt eine Tatsache, keine Absicht, keinen Wunsch, keine Prognose.
- Es ist aus Sicht der Domäne verständlich und nachvollziehbar.
- Es benennt, was passiert ist, und nicht, wie es implementiert wurde.
Beispiel:
- Nicht „book created“, sondern „Buch wurde in den Bestand aufgenommen“.
- Nicht „book updated“, sondern „Buch wurde verliehen“.
Kein technisches Artefakt
Events sind keine Logzeilen, keine Debug-Ausgaben und keine reinen Transportformate. Sie bilden tatsächlich den fachlichen Kern dessen, was das System ausdrücken soll. Man kann sich ein Event daher wie einen Eintrag in einem Geschäftsbuch vorstellen – etwas, das dauerhaft dokumentiert, was geschehen ist, und auf dessen Basis Entscheidungen getroffen werden.
Wer Events als rein technische Hilfsmittel betrachtet, läuft Gefahr, ihre Bedeutung zu verwässern. Stattdessen sollten Events präzise benannt und stabil gehalten werden – denn sie bilden die langfristige Wahrheit des Systems.
Stabilität und Entwicklung
Events sind in der Regel unveränderlich. Sie werden erzeugt, gespeichert und danach nicht mehr verändert. Das bedeutet: Ein einmal veröffentlichtes Event ist Teil der Historie und muss als solches erhalten bleiben. Wenn sich Anforderungen ändern, sollte man nicht bereits vorhandene Events „anpassen“, sondern ergänzende Events einführen.
Diese Stabilität erlaubt es, Projections zu rekonstruieren, Prozesse zu debuggen und neue Anforderungen auf Basis der bestehenden Historie umzusetzen – zum Beispiel durch das Nachverarbeiten alter Events mit neuer Logik.
Veröffentlichung und Reaktion
Wenn ein Event entstanden ist, kann es nicht nur im eigenen System genutzt werden – sondern auch veröffentlicht werden. Oft geschieht das über eine Message Queue oder einen Event Bus. Dadurch können andere Teile des Systems (oder andere Systeme) darauf reagieren, ohne dass es eine direkte Kopplung gibt.
Das bedeutet: Events sind nicht nur Daten – sie sind ein Vertrag. Ein Versprechen, dass ein bestimmter Zustand eingetreten ist – und dass man sich darauf verlassen kann.
Was kommt als Nächstes?
Im nächsten Teil schauen wir uns an, was Event-getriebene Architektur wirklich bedeutet – und was häufig damit verwechselt wird. Denn nicht überall, wo Events draufstehen, ist auch Event-getriebene Architektur drin.
(mai)
Entwicklung & Code
GUI-Frameworks für .NET – Teil 5: Wisej.NET und Avalonia
Nicht nur Microsoft bietet GUI-Frameworks für .NET an. In den letzten beiden Teilen dieser Artikelserie soll es um die Vor- und Nachteile solcher Frameworks gehen. Dieser Beitrag stellt zunächst Wisej.NET vor, das Windows Forms sehr ähnlich ist, aber plattformneutral läuft. Der zweite plattformneutrale Kandidat, Avalonia, ist hingegen an Windows Presentation Foundation (WPF) angelehnt.
Dr. Holger Schwichtenberg ist Chief Technology Expert bei der MAXIMAGO-Softwareentwicklung. Mit dem Expertenteam bei www.IT-Visions.de bietet er zudem Beratung und Schulungen im Umfeld von Microsoft-, Java- und Webtechniken an. Er hält Vorträge auf Fachkonferenzen und ist Autor zahlreicher Fachbücher.
Wisej.NET
Wisej.NET – von 2016 bis 2022 in den Versionen 1.x und 2.x nur Wisej genannt – ist ein Rapid-Application-Development-Werkzeug für HTML-basierte Web- und Hybridanwendungen. Dahinter steht die 1998 gegründete US-amerikanische Firma Ice Tea Group LLC mit Sitz in Washington, D.C., die aus dem Umfeld von Gupta/Centura stammt, sich aber seit vielen Jahren auf dem .NET-Markt etabliert hat.
Wisej.NET verfolgt die gleiche Grundidee wie Blazor Server, das heißt, die Anwendung läuft auf dem Server, empfängt Benutzeraktionen und sendet Änderungen der HTML-Oberfläche über das Netzwerk zum Webbrowser. Der Chefentwickler von Wisej.NET, Gianluca Pivato, hält darauf sogar ein Patent unter dem Titel „Pixel perfect real-time web application framework„, bei dem man sich fragen kann, ob Blazor Server nicht dagegen verstößt.
Serverseitig lief Wisej.NET in den Versionen 1.0 und 2.0 nur auf dem klassischen .NET Framework auf Windows. Seit Version 3.0 ist auch das moderne .NET möglich, mit Windows oder Linux als Server. Der Serverprozess basiert dann auf ASP.NET Core. Als Programmiersprachen sind neben C# auch Visual Basic .NET und F# möglich. Auf der Clientseite setzt Wisej.NET auf das von der deutschen Firma 1&1 entwickelte JavaScript-basierte Webfrontend-Framework qooxdoo (gesprochen „kuckst du“).
Bei der Datenübertragung zwischen Server und Client ist neben WebSockets auch HTTP(S) möglich. Die Übertragung der DOM-Unterschiede vom Server zum Client ist effizienter als bei Blazor Server. Allerdings wird beim ersten Seitenaufruf mehr in den Browser geladen. Genau wie Blazor Server ist Wisej.NET nicht offlinefähig und alle Nutzer teilen sich die Serverressourcen. Wisej.NET kommt aber besser mit einem Reload im Browser zurecht als Blazor Server, da es den Anwendungszustand erhält. Bei Blazor Server geht der Anwendungszustand derzeit bei einem Reload immer verloren, wobei Microsoft für .NET 10.0 hier eine Verbesserung plant.
(Bild: coffeemill/123rf.com)
Das Programm steht fest: Am 18. November 2025 zeigt die Online-Konferenz betterCode() .NET 10.0 von iX und dpunkt.verlag in Kooperation mit www.IT-Visions.de die Neuerungen im LTS-Release – bei SDK, Runtime und der neuen Sprachversion C# 14.0 sowie bei der Windows-, Cross-Platform- und Webentwicklung.
Wisej.NET ist Windows Forms sehr ähnlich. Es gibt sogar die Möglichkeit, bestehende Windows-Forms-Anwendungen nach Wisej.NET zu migrieren, also klassische Windows-Desktop-Anwendungen in den Browser und auf Mobilgeräte zu bringen. Im Wesentlichen muss man Assembly-Referenzen und Namensräume (aus dem Namensraum System.Windows.Forms
wird Wisej.Web
) ändern, was per Suchen und Ersetzen geht. Im Detail gibt es aber kleinere Unterschiede.
Abbildung 1 zeigt eine Windows-Forms-Anwendung und das nach Wisej.NET migrierte Pendant: Der modale Dialog graut das Hauptfenster aus, was bei Windows Forms nicht automatisch passiert. Die Steuerelemente sind bei Wisej.NET moderner und im Standard größer. Das Browserfenster in der Abbildung läuft dabei nur mit Zoomfaktor 80 Prozent.
Auch bei der API gibt es Unterschiede, die schon bei der kleinen Beispielanwendung in Abbildung 1 auffallen. Zum Beispiel setzt man in Wisej.NET die Hintergrundfarbe im GridView
-Steuerelement mitthis.C_Tasks.BackColor = SystemColors.GradientInactiveCaption
statt dem in Windows Forms verwendeten this.C_Tasks.BackgroundColor = SystemColors.GradientInactiveCaption
. Wisej.NET ist hier einheitlicher, denn BackColor
verwenden die meisten Steuerelemente in Windows Forms.
Bei den Steuerelementen ToolBar
, StatusBar
, ContextMenu
, MainMenu
und MenuBar
ist Wisej.NET ebenfalls konsistenter, zum Beispiel wird aus
C_Status.AutoSize = true;
ein
C_Status.AutoSize = Wisej.Web.StatusBarPanelAutoSize.Spring;
In Windows Forms hat das Steuerelement StatusBarPanel
die Eigenschaft StatusBarPanelAutoSize
, dahingegen ToolStripStatusLabel
nur ein AutoSize = true
oder false
.
Auch nach dem Setzen der Farbe für den GridView
-Hintergrund und den zugehörigen Zellhintergrund auf hellblau erscheinen die Zeilen bei Wisej.NET immer noch in Weiß (siehe rechts in Abbildung 1). Um das zu lösen, muss man zusätzlich in das Theme in der Datei App.mixin.theme eingreifen:
{
"colors": {
"table-row-background": "#d7e4f2"
}
}
Ein solches Theming gibt es in Windows Forms gar nicht, zumindest nicht ohne Drittanbieterbibliothek. Abbildung 2 zeigt im Hintergrund das GridView
-Steuerelement mit einheitlicher Hintergrundfarbe, wie in der Windows-Forms-Anwendung links in Abbildung 1.
Eine Windows-Forms-Anwendung (links) wurde nach Wisej.NET migriert und läuft nun im Browser (rechts) (Abb. 1).
Für die Migration von Windows Forms zu Wisej.NET gibt es auch ein Werkzeug, das man allerdings nicht erwerben kann. Die Migration ist vielmehr eine angebotene Dienstleistung, an deren Ende der Auftraggeber eine lauffähige Wisej.NET-Anwendung inklusive Quellcode erhält. In unseren Gefilden macht diese Migration die Firma Fecher GmbH. Sie bietet die Migration nach Wisej.NET nicht nur von Windows Forms, sondern auch von WPF, Visual Basic 6, Microsoft Access und Gupta an. Projekte für die Migration von Windows-Lösungen bietet auch die Ice Tea Group direkt an.
Anders als Blazor Server hat Wisej.NET ein Session-Timeout nach 120 Sekunden Inaktivität und zeigt dann einen einminütigen Countdown zum Beenden der Anwendung (siehe Abbildung 2). Wenn der Benutzer oder die Benutzerin nicht reagiert, wird die Wisej.NET-Anwendung beendet und alle Ressourcen auf dem Webserver freigegeben. Entwicklerinnen und Entwickler können diese Zeit anpassen und wie bei Blazor Server auf unbegrenzt setzen, also solange der Browser lebt. Auch der Countdown lässt sich anpassen.
Anders als bei der aktuellen Blazor-Server-Version in .NET 9.0 können Entwicklerinnen und Entwickler in dem Wisej.NET-Serverprozess auch einstellen, dass Sitzungen nach Beenden des Browsers erhalten bleiben und auf Basis einer im Local Storage des Browsers abgelegten Session-ID später wieder aufgenommen werden können.
Session-Timeout bei Wisej.NET (Abb. 2)
Wisej.NET bietet für die Entwicklung einen in Visual Studio integrierten WYSIWYG-Designer an, den man über die Visual Studio Extensions oder die Wisej-Website installieren kann und der dem Windows-Forms-Designer sehr ähnlich ist (siehe Abbildung 3). Genau wie bei Windows Forms generiert der Designer kein Markup, sondern Programmcode.
Der WYSIWYG-Designer von Wisej.NET sieht aus wie der Windows-Forms-Designer und hat die gleiche Funktionsweise (Abb. 3).
Wisej.NET liefert zahlreiche Steuerelemente im Grundpaket (.NET-Namensraum Wisej.Web
) oder als Extension, auch höherwertige Steuerelemente wie ein editierbares Datagrid, verschiedene Diagrammtypen, einen Kalender, ein Ribbon, Anzeige von Microsoft-Office-Dokumenten sowie ein Theming (dafür gibt es einen eigenen Theme Builder als Windows-Anwendung). Die Steuerelemente kann man sich auf einer webbasierten Demonstrationsseite ansehen.
Entwicklerinnen und Entwickler müssen sich bei der Webentwicklung mit Wisej.NET nicht mit HTML, CSS und JavaScript auseinandersetzen, denn es ermöglicht eine komponentenbasierte Abstraktion von den Webtechniken, aus denen das Document Object Model generiert wird. Freilich kann man Inseln von HTML erschaffen; dafür gibt es Wisej.NET-Steuerelemente wie HtmlPanel
und einen Extender für JavaScript und CSS. Entwicklerinnen und Entwickler können also wahlweise selbst JavaScript schreiben.
Es gibt zwar keine dedizierte Komponentenbibliothek eines Drittanbieters für Wisej.NET, aber das GUI-Framework kann beliebige Web-Komponentenbibliotheken nutzen, die auf HTML, CSS und JavaScript basieren (zum Beispiel jQuery, DevExpress DevExtreme, Syncfusion EJ1/EJ2, Telerik Kendo UI, Infragistics Ignite UI und TextControl). Premium Extensions vereinfachen deren Integration. Genau wie Microsoft mit seinen Smart Components arbeitet auch Wisej.NET an der KI-Integration in die Steuerelemente.
Neben dem Deployment auf einen Webserver kann Wisej.NET auch eine ausführbare Datei für das Self-Hosting erzeugen. AOT-Kompilierung ist bisher nicht möglich.
Genau wie Blazor lässt sich Wisej.NET in .NET-MAUI-Anwendungen einbetten und dann ohne Webbrowser direkt auf Windows, macOS, iOS und Android betreiben (siehe Wisej.NET Hybrid). Diese hybriden Wisej.NET-Anwendungen können auch offline ohne einen Webserver laufen.
Wisej.NET hat einige namhafte Kunden (siehe Case Studies auf der Wisej-Website und unter madewithwisej.com) und ist regelmäßig auf deutschen Entwicklerkonferenzen vertreten. Man braucht für jeden Entwickler und jede Entwicklerin eine Lizenz, die je nach Leistungsumfang zwischen 1190 und 1690 Dollar kostet, wobei es Mengenrabatte gibt. Für nicht-kommerzielle Zwecke ist eine kostenfreie Community-Lizenz verfügbar. Der Betrieb der Wisej.NET-Anwendung erfordert eine Serverlizenz. Dabei reicht das Spektrum von einer kostenfreien Lizenz für bis zu 100 Clients ohne Premium Extensions und ohne Support bis hin zu einer Lizenz für rund 29.000 Dollar für bis zu 10.000 Clients.
Entwicklung & Code
Neu in .NET 9.0 [29]: Verbesserung beim Source Generator für reguläre Ausdrücke
Microsoft verwendet in .NET 9.0 das neue Sprachfeature „Partielle Properties“ aus C# 13.0 und erlaubt im Source Generator für reguläre Ausdrücke, der vor zwei Jahren eingeführt wurde. Darüber habe ich in meiner Blogserie zu .NET 7.0 berichtet. Neu in .NET 9.0 ist, dass Entwicklerinnen und Entwickler die Annotation [GeneratedRegex]
nun nicht mehr nur für Methoden, sondern auch Properties verwenden können.
Dr. Holger Schwichtenberg ist technischer Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland.
Folgendes Codebeispiel zeigt das Vorgehen am Beispiel der E-Mail-Adressen-Validierung:
using System.Text.RegularExpressions;
namespace NET9_Console.FCL90;
public partial class Checker_Alt // Partielle Klasse
{
[GeneratedRegex(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")] // ALT: Auf Methode
// Partielle Methode, die dann von SG implementiert wird
public partial Regex EMailRegEx();
}
public partial class Checker_Neu // Partielle Klasse
{
[GeneratedRegex(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")] // NEU: Auf Property
// Partielles Property mit Getter, der dann von SG implementiert wird
public partial Regex EMailRegEx { get; }
}
public class FCL9_RegExSourceGenerator
{
public void Run()
{
CUI.Demo(nameof(FCL9_RegExSourceGenerator));
// Aufruf der partiellen Methode
Console.WriteLine(new Checker_Alt().EMailRegEx().IsMatch("max@mustermann.de"));
// Aufruf des partiellen Properties
Console.WriteLine(new Checker_Neu().EMailRegEx.IsMatch("max@mustermann.de"));
}
}
Der Source Generator erzeugt beim Kompilieren zur Entwicklungszeit den Inhalt der Methode und den Getter des partiellen Properties. Den generierten Programmcode findet man in der Datei RegexGenerator.g.cs im Ordner C:\Users\Benutzername\AppData\Local\Temp\VSGeneratedDocuments\GUID.
(rme)
-
Online Marketing & SEOvor 1 Monat
TikTok trackt CO₂ von Ads – und Mitarbeitende intern mit Ratings
-
Apps & Mobile Entwicklungvor 1 Monat
Metal Gear Solid Δ: Snake Eater: Ein Multiplayer-Modus für Fans von Versteckenspielen
-
Digital Business & Startupsvor 2 Wochen
80 % günstiger dank KI – Startup vereinfacht Klinikstudien: Pitchdeck hier
-
UX/UI & Webdesignvor 1 Monat
Philip Bürli › PAGE online
-
Social Mediavor 1 Monat
Aktuelle Trends, Studien und Statistiken
-
Social Mediavor 4 Wochen
LinkedIn Feature-Update 2025: Aktuelle Neuigkeiten
-
Online Marketing & SEOvor 1 Monat
#WantaFanta: Warum Fanta und Nico Santos der Gen Z Wünsche erfüllen
-
Social Mediavor 1 Monat
“Wir haben doch nichts zu erzählen…” – 3 Tricks für neue Social Media Content Ideen