I have solved my problem in the way I will describe below but I want to find the best way.
Typescript has generic type Readonly that solved this problem.
Thanks for your suggests I used folowing codes (.net6) :
public interface IReadOnlyAbility
{
bool IsReadOnly { get; }
void ReadOnly();
public T CheckForSetValue<T>(T PropertyValue)
{
if (IsReadOnly)
throw new ReadOnlyException();
return PropertyValue;
}
}
public interface IUser : IReadOnlyAbility
{
string UserName { get; set; }
string Password { get; set; }
}
public class User : IUser
{
private string userName = string.Empty;
private string password = string.Empty;
public bool IsReadOnly { get; private set; }
public string UserName
{
get => userName;
set => userName = CheckForSetValue(value);
}
public string Password
{
get => password;
set => password = CheckForSetValue(value);
}
protected T CheckForSetValue<T>(T Value) => ((IReadOnlyAbility)this).CheckForSetValue(Value);
public void ReadOnly() => IsReadOnly = true;
}
Then I added dependency injection >>> Services.AddTransient<IUser, User>();
Now , I used it :
var user = Services.GetService<IUser>();
user.UserName = "UserName";
user.Password = "Password";
user.ReadOnly();
I checked the proposed method, calling TrySetValue for each initialization was a bit confusing and I defined an alternative solution using Lib.Harmony for my project, which seems to me to be cleaner in terms of coding.
i use folowing nuget
NuGet\Install-Package Lib.Harmony -Version 2.2.2
IReadOnlyAbility is an interface whose implementation makes all configurable public properties of an object read-only by default.
public interface IReadOnlyAbility
{
bool IsReadOnly { get; internal set; }
void ReadOnly() ;
}
By using ReadOnlyAbilityAttribute it is possible to set attributes that should not be read-only.
[AttributeUsage(AttributeTargets.Property)]
public class ReadOnlyAbilityAttribute : Attribute
{
public bool Check { get; }
public ReadOnlyAbilityAttribute(bool check) => Check = check;
}
ReadOnlyAbilityInstaller is responsible for adding read-only checking code for classes that implement IReadOnlyAbility
public sealed class ReadOnlyAbilityInstaller
{
private readonly Lazy<Harmony> HarmonyInstance = new Lazy<Harmony>(() => new Harmony($"Harmony{Guid.NewGuid().ToString().Replace("-", string.Empty)}"));
public void Install(Assembly assembly)
{
var readOnlyAbilityType = typeof(IReadOnlyAbility);
var readOnlyAbilityTypes = assembly.GetTypes().Where(readOnlyAbilityType.IsAssignableFrom).Where(q => q.IsClass || q.IsValueType).ToList();
var prefix = typeof(ReadOnlyAbilityInstaller).GetMethod(nameof(PrefixMethod), BindingFlags.NonPublic | BindingFlags.Static);
var prefixHarmonyMethod = new HarmonyMethod(prefix);
foreach (var type in readOnlyAbilityTypes)
{
var properties = type.GetProperties().Where(q => q.GetCustomAttribute<ReadOnlyAbilityAttribute>()?.Check != false)
.Where(q => q.SetMethod is not null)
.Select(q => q.SetMethod!)
.Where(q => q.IsPrivate == false)
.ToList();
foreach (var property in properties)
HarmonyInstance.Value.Patch(property, prefixHarmonyMethod);
}
}
private static bool PrefixMethod(IReadOnlyAbility __instance, MethodBase __originalMethod)
{
if (__instance.IsReadOnly)
throw new ReadOnlyException($"Current instance is readonly cause «{__originalMethod.Name.Replace("set_", string.Empty)}» can not be change");
return true;
}
}
Now need to install its :
internal static class Program
{
[STAThread]
static void Main()
{
var assembly = Assembly.GetExecutingAssembly();
var readOnlyAbilityInstaller = new ReadOnlyAbilityInstaller();
readOnlyAbilityInstaller.Install(assembly);
/*other source code*/
}
}
For example :
public class ServiceConfig : IReadOnlyAbility
{
public string IP { get; set; }
public int Port { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool IsReadOnly { get; private set; }
public void ReadOnly() => IsReadOnly = true;
}
public class Service
{
public readonly ServiceConfig Config;
public Service(ServiceConfig config)
{
Config = config;
Config.ReadOnly();
}
}