How to check record in c# 9 in NET 5 is immutable at runtime - immutability

Record is a new feature in c#9, Net 5
It's said
If you want the whole object to be immutable and behave like a value, then you should consider declaring it as a record
Creating a record in c#9 , NET 5:
public record Rectangle
{
public int Width { get; init; }
public int Height { get; init; }
}
Then instantiating it:
var rectangle = new Rectangle (20,30);
Trying to change the value:
rectange.Width=50; //compiler error
Compiler raise the error:
error CS8852: Init-only property or indexer 'Rectangle.Width' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.
That is right and insure that the record is immutable.
Using a method like to test IsImmutable type give false, because in record there is no generated readonly properties.
How to check the record in c# 9, Net 5 is immutable at runtime or even it has init property?

A record is indeed mutable at runtime. This is intentional, is it means most serializer frameworks work without updating.
It is however possible to check if a property is initonly by checking:
public static bool IsInitOnly(PropertyInfo propertyInfo)
{
return propertyInfo?.SetMethod.ReturnParameter
.GetRequiredCustomModifiers()
.Any(x => x.FullName == _isExternalInitName)
?? false;
}
private static string _isExternalInitName =
typeof(System.Runtime.CompilerServices.IsExternalInit).FullName;

I don't think that it's possible to check for immutability at runtime.
Here's some of the generated code for your record. You can see that both properties have a public setter.
public class Rectangle : IEquatable<Rectangle>
{
[CompilerGenerated]
private readonly int <Width>k__BackingField;
[CompilerGenerated]
private readonly int <Height>k__BackingField;
protected virtual Type EqualityContract
{
[CompilerGenerated]
get
{
return typeof(Rectangle);
}
}
public int Width
{
[CompilerGenerated]
get
{
return <Width>k__BackingField;
}
[CompilerGenerated]
set
{
<Width>k__BackingField = value;
}
}
public int Height
{
[CompilerGenerated]
get
{
return <Height>k__BackingField;
}
[CompilerGenerated]
set
{
<Height>k__BackingField = value;
}
}
The following code will compile and run without errors.
var rect = new Rectangle { Height = 1, Width = 2 };
typeof(Rectangle).GetProperty("Height").SetValue(rect, 5);
Console.Write(rect.Height);
//Prints 5
At runtime the init accessor is just a regular setter. It's only at compile time that a check is made to only allow init accessor to be called during object initialization.
So I don't see any way to check at runtime that Rectangle is immutable.

Related

Winforms: Change background colour of an element depending on value of a variable

I have a small project here in visual studio 2022, a Winforms c++/CLR project.
It is a pretty simple project and form, but I have an issue with the following:
In the Winforms app I use a small panel to display the status of some input that I get.
I want to change the background colour of the panel depending on the status of that variable.
So for example if the variable is 0, the background colour should be red, if the value of the variable is 1, it should be green.
I can easily make this work, when this is coupled to a trigger event from the user, such as mouseclick, mousehover, ....
But how do I make this work dynamically during runtime, such that even while the variable changes during runtime, the backgroundcolour of the panel changes with it too, without the need of a user input event such as a mouseclick, ... ?
What kind of class need to be made in order to make this work?
An example that shows how to bind the Property of a Control to the Property of a managed class object, implementing the INotifyPropertyChanged interface, used to trigger value change notifications.
In the ColorTrigger class, when the value of an int Property is changed, this also changes the value returned by a Property of Type Color and triggers a PropertyChanged notification.
This causes all Controls bound to the class object to read again the value of the Property specified in the binding:
somePanel->DataBindings->Add(
"BackColor", trigger, "ColorValue", false, DataSourceUpdateMode::OnPropertyChanged
);
Here, DataBindings.Add() binds the BackColor Property of a Panel Control to the ColorValue Property of the ColorTrigger class.
#pragma once
#include "ColorTrigger.h"
public ref class SomeForm : public System::Windows::Forms::Form
{
// Initialize with default values
private: ColorTrigger^ trigger = gcnew ColorTrigger();
public: SomeForm(void) {
InitializeComponent();
somePanel->DataBindings->Add("BackColor", trigger, "ColorValue", false, DataSourceUpdateMode::OnPropertyChanged);
}
Or pass different Colors to the Constructor of the ColorTrigger class:
// [...]
private: ColorTrigger^ trigger;
public: SomeForm(void) {
InitializeComponent();
trigger = gcnew ColorTrigger(gcnew array<Color> {
Color::Magenta, Color::Cyan, Color::Orange
});
somePanel->DataBindings->Add("BackColor", trigger, "ColorValue", false, DataSourceUpdateMode::OnPropertyChanged);
}
Now, when you assign a new value to the Value Property of the ColorTrigger class, the Color returned by the ColorValue Property changes accordingly and a notification is sent, causing the Binding to update and with it the Property of Controls that share the binding.
ColorTrigger managed class:
Note that this line in the Property setter:
colorIdx = Math::Max(Math::Min(value, colors->Length - 1), 0);
trims the value set in the range (0, [ColorArray].Length - 1).
You may want to throw instead, if value is outside the bounds of the array.
#pragma once
using namespace System;
using namespace System::ComponentModel;
using namespace System::Drawing;
#define nameof(_propname) #_propname
public ref class ColorTrigger : INotifyPropertyChanged
{
public:
virtual event PropertyChangedEventHandler^ PropertyChanged;
private:
array<Color>^ colors;
int colorIdx = 0;
public:
ColorTrigger(void) {
this->ColorTrigger::ColorTrigger(
gcnew array<Color> {Color::Green, Color::Orange, Color::Red}
);
}
ColorTrigger(array<Color>^ colorArr) {colors = colorArr;}
public :
property int Value {
int get() { return colorIdx; }
void set(int value) {
if (value != colorIdx) {
colorIdx = Math::Max(Math::Min(value, colors->Length - 1), 0);
ColorValue = colors[colorIdx];
}
}
}
property Color ColorValue {
Color get() { return colors[colorIdx]; }
private : void set(Color value) {
OnPropertyChanged(nameof(ColorValue));
}
}
void OnPropertyChanged(String^ prop) {
PropertyChanged(this, gcnew PropertyChangedEventArgs(prop));
}
};

Can I call a function from the base class which return bool from derived class

I have the following base class:
class node_layer_manager_t : public layer_manager_t
{
protected:
//Devices
trx_t trx;
private:
std::vector<string> trx_dump_labels;
public:
node_layer_manager_t( xml::node_t& params );
~node_layer_manager_t();
virtual bool set_profile(void) override;
}
I created the following derived class:
class node_layer_manager_with_rad_t : public node_layer_manager_t
{
protected:
//Devices
radio_t radio;
public:
node_layer_manager_with_rad_t(xml::node_t& params );
~node_layer_manager_with_rad_t();
virtual bool set_profile(void) override;
virtual void radio_monitoring_job_function(void);
intervalues_t<double> radio_tmp;
ushort duration_seconds_for_radio_monitoring;
};
I want it so that the set profile will execute the set_profile of the base class and in addition some other action.
Can I just write it this way?
bool node_layer_manager_with_rad_t::set_profile(void)
{
bool success;
node_layer_manager_t::set_profile();
try
{
string_t profile_tag = "logs/trx_dump/node:"+get_id();
dev_tx = profile->get_decendant(profile_tag.c_str());
cout<<"sarit id= "<< get_id()<<endl;
success = true;
}
catch(...)
{
cout<<"sarit profile error: "<<endl;
success = false;
}
return success; //**
}
**Or should I reurn the follwing:
return (success && node_layer_manager_t::set_profile());
If you have to call parent set_profile regardless what you have to do in derived class, you should adopt design which take care about this constraint.
Typically, you should mark based class set_porfile as final and manage call of a dedicated derived class method inside based class:
class node_layer_manager_t : public layer_manager_t
{
protected:
....
// set_profile actions of derived class
// proposed a default without side effect implementation if
// derived class doesn't need to overload this.
virtual bool set_profile_child() { return true; };
private:
....
public:
.....
// Manage here call of derived
virtual bool set_profile() override final
{
// actions before derived specific actions
....
// Call specific derived class actions
bool success = set_profile_child();
// actions after derived specific actions
if (success)
{
//do based class action
}
return success;
}
}
and in child:
class node_layer_manager_with_rad_t : public node_layer_manager_t
{
protected:
....
public:
virtual bool set_profile_child() override;
};
// Manage only there own action, regardless of needs of based class
bool node_layer_manager_with_rad_t::set_profile(void)
{
try
{
// Do what you're in charge, and only what you're in charge!
}
catch(...)
{
cout<<"sarit profile error: "<<endl;
success = false;
}
return success; //**
}
With this kind of design, each class do only what it have to manage, and only its. Derived class doesn't have to deal with needs of based class.
If you want to offer to your derived class ability to decided if code is executed before or after generic behavior, you can replace or add to set_profile_child() two methods: bool pre_set_profile() and bool post_set_profile()
At first, you haven't declared success anywhere (so actually, this is not a mcve, the code should not compile as is).
Still I get it - and tThe answer is: it depends on what you actually want to do...
Do you want to call the super class first or after the sub class code? Your example implies the former, your alternative the latter. Do you want to abort if the super class function fails or still execute your code?
Your inital example calls the super class function, ignores the result and does its own stuff afterwards.
This calls the super class function first and continues only on success:
bool success = node_layer_manager_t::set_profile();
if(success)
{
try { /*...*/ } // <- no need to set success to true, it is already
catch(...) { /*...*/ success = false; }
}
This executes both, but combines the result:
bool success = node_layer_manager_t::set_profile();
try { /*...*/ } // <- do not modify success, must remain false if super class failed!
catch(...) { /*...*/ success = false; }
Your alternative hints to executing the sub class code first and only call the super class function, if nothing went wrong.
Any of these approaches might be appropriate, none of them might be. You have to get a clear image of what your requirements are - and then implement the code such that your needs are satisfied...

JFace TreeView not launching when Input is a String

I'm trying launch a simple JFace Tree.
It's acting really strange however. When I setInput() to be a single String, the tree opens up completely blank. However, when I set input to be a String array, it works great.
This has nothing to do with the LabelProvider or ContentProvider since these behave the same no matter what (it's a really simple experimental program).
setInput() is officially allowed to take any Object. I am confused why it will not take a String, and knowing why may help me solve my other problems in life.
Setting a single String as input:
TreeViewer treeViewerLeft = new TreeViewer(shell, SWT.SINGLE);
treeViewerLeft.setLabelProvider(new TestLabelProvider());
treeViewerLeft.setContentProvider(new TestCompareContentProvider());
treeViewerLeft.expandAll();
treeViewerLeft.setInput(new String("Stooge"));
Setting an array of Strings:
TreeViewer treeViewerLeft = new TreeViewer(shell, SWT.SINGLE);
treeViewerLeft.setLabelProvider(new TestLabelProvider());
treeViewerLeft.setContentProvider(new TestCompareContentProvider());
treeViewerLeft.expandAll();
treeViewerLeft.setInput(new String[]{"Moe", "Larry", "Curly"});
The second works, and launches a tree using the following providers:
public class TestCompareContentProvider extends ArrayContentProvider implements ITreeContentProvider {
public static int children = 0;
public Object[] getChildren(Object parentElement) {
children++;
if (children > 20){
return null;
}
return new String[] {"Moe", "Larry", "Curly"};
}
public Object getParent(Object element) {
return "Parent";
}
public boolean hasChildren(Object element) {
if (children >20){
return false;
}
return true;
}
}
and
public class TestLabelProvider extends LabelProvider {
public String getText(Object element){
return "I'm something";
}
public Image getImage(Object element){
return null;
}
}
You've inherited getElements from the ArrayContentProvider and that only works with arrays. You should override this method.
I don't think you need to extend ArrayContentProvider at all.

Caliburn Micro Communication between ViewModels

hopefully you can help me. First of all, let me explain what my problem is.
I have two ViewModels. The first one has e.g. stored information in several textboxes.
For example
private static string _tbxCfgLogfile;
public string TbxCfgLogfile
{
get { return _tbxCfgLogfile; }
set
{
_tbxCfgLogfile = value;
NotifyOfPropertyChange(() => TbxCfgLogfile);
}
}
The other ViewModel has a Button where i want to save this data from the textboxes.
It does look like this
public bool CanBtnCfgSave
{
get
{
return (new PageConfigGeneralViewModel().TbxCfgLogfile.Length > 0 [...]);
}
}
public void BtnCfgSave()
{
new Functions.Config().SaveConfig();
}
How can i let "CanBtnCfgSave" know that the condition is met or not?
My first try was
private static string _tbxCfgLogfile;
public string TbxCfgLogfile
{
get { return _tbxCfgLogfile; }
set
{
_tbxCfgLogfile = value;
NotifyOfPropertyChange(() => TbxCfgLogfile);
NotifyOfPropertyChange(() => new ViewModels.OtherViewModel.CanBtnCfgSave);
}
}
It does not work. When i do remember right, i can get the data from each ViewModel, but i cannot set nor Notify them without any effort. Is that right? Do i have to use an "Event Aggregator" to accomplish my goal or is there an alternative easier way?
Not sure what you are doing in your viewmodels - why are you instantiating viewmodels in property accessors?
What is this line doing?
return (new PageConfigGeneralViewModel().TbxCfgLogfile.Length > 0 [...]);
I can't be sure from your setup as you haven't mentioned much about the architecture, but sincce you should have an instance of each viewmodel, there must be something conducting/managing the two (or one managing the other)
If you have one managing the other and you are implementing this via concrete references, you can just pick up the fields from the other viewmodel by accessing the properties directly, and hooking the PropertyChanged event of the child to notify the parent
class ParentViewModel : PropertyChangedBase
{
ChildViewModel childVM;
public ParentViewModel()
{
// Create child VM and hook up event...
childVM = new ChildViewModel();
childVM.PropertyChanged = ChildViewModel_PropertyChanged;
}
void ChildViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// When any properties on the child VM change, update CanSave
NotifyOfPropertyChange(() => CanSave);
}
// Look at properties on the child VM
public bool CanSave { get { return childVM.SomeProperty != string.Empty; } }
public void Save() { // do stuff }
}
class ChildViewModel : PropertyChangedBase
{
private static string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
_someProperty = value;
NotifyOfPropertyChange(() => SomeProperty);
}
}
}
Of course this is a very direct way to do it - you could just create a binding to CanSave on the child VM if that works, saving the need to create the CanSave property on the parent

MVVM - View loading and eventhandling

In my windows phone app, I need to track some events to get a good flow. But I'm not sure how to handle them in good sequence.
What needs to be done at startup of the app:
Main view is loaded and corresponding view model instantiated
In the constructor of the view model I initiate a login sequence that signals when completed with an eventhandler
Now when the login sequence has finished AND the view is completely loaded I need to startup another sequence.
But here is the problem, the order of these 2 events 'completing' is not always the same...
I've use the EventToCommand from MVVMLight to signal the view model that the view has 'loaded'.
Any thoughts on how to synchronize this.
As you should not use wait handles or something similar on the UI thread. You will have to sync the two method using flags in your view model and check them before progressing.
So, implement two boolean properties in your view model. Now when the login dialog is finished set one of the properties (lets call it IsLoggedIn) to true, and when the initialization sequence is finished you set the other property (how about IsInitialized) to true. The trick now lies in the implementation of the setter of these two properties:
#region [IsInitialized]
public const string IsInitializedPropertyName = "IsInitialized";
private bool _isInitialized = false;
public bool IsInitialized {
get {
return _isInitialized;
}
set {
if (_isInitialized == value)
return;
var oldValue = _isInitialized;
_isInitialized = value;
RaisePropertyChanged(IsInitializedPropertyName);
InitializationComplete();
}
}
#endregion
#region [IsLoggedIn]
public const string IsLoggedInPropertyName = "IsLoggedIn";
private bool _isLoggedIn = false;
public bool IsLoggedIn {
get {
return _isLoggedIn;
}
set {
if (_isLoggedIn == value)
return;
var oldValue = _isLoggedIn;
_isLoggedIn = value;
RaisePropertyChanged(IsLoggedInPropertyName);
InitializationComplete();
}
}
#endregion
public void InitializationComplete() {
if (!(this.IsInitialized && this.IsLoggedIn))
return;
// put your code here
}
Alternatively you can remove the InitializationComplete from the setters and change InitializationComplete to:
public void InitializationComplete() {
// put your code here
}
Then subscribe to the 'PropertyChanged' event use the following implementation:
private void Class1_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
if (e.PropertyName == IsInitializedPropertyName || e.PropertyName == IsLoggedInPropertyName) {
if (this.IsInitialized && this.IsLoggedIn)
InitializationComplete();
}
}

Resources