mercredi 13 décembre 2023

How to match typeof(

I'm using reflection to determine how to format properties on an object.

Here's a dictionary of formatters:

var formatters = new Dictionary<Type, Func<object, object>>()
{
    { typeof(Enum), x => ((Enum)x)?.FormatEnum() },
    { typeof(DateTime?), x => ((DateTime?)x)?.ToShortDateString() },
    { typeof(string), x => x.ToString().ToLower() }
};

Most types will work fine, however when I get the type of my nullable enum TaskStatus, the type is not "Enum", it's the name of the type.

I noticed the base type is "Enum", however I don't want to have to check the base type since most other types will work just fine with the type.

For example, checking the type of string works fine:

typeof(string).FullName = "System.String"

But for the TaskStatus enum:

typeof(Enums.TaskStatus).FullName = "Enums.TaskStatus"

So typeof(Enum) != typeof(Enums.TaskStatus) which is what is preventing the dictionary from finding a match.

Is there any way to find a match in the formatters dictionary without having to put in all the different nullable enum type names I am using?

Edit:

Here's my entire code:

public class NoteHistory
{
    [JsonProperty("Status")]
    public TaskStatus? TaskStatus { get; set; }
    [JsonProperty("CreatedDate")]
    public DateTime? CreatedDate { get; set; }
    [JsonProperty("Description")]
    public string Description { get; set; }
    public bool IsDeleted { get; set; } // not interested in comparing this
}

public enum TaskStatus
{
    ToDo,
    Done
}

public static void DisplayValues()
{
    var noteHistory1 = new NoteHistory
    {
        TaskStatus = TaskStatus.ToDo
    };

    var noteHistory2 = new NoteHistory
    {
        TaskStatus = TaskStatus.Done
    };

    // Get all properties with JsonProperty attr.
    var propertiesToCompare = typeof(NoteHistory)
        .GetProperties()
        .Where(x => x.CustomAttributes
            .Any(y => y.AttributeType.Name == nameof(JsonPropertyAttribute)));

    var comparers = new Dictionary<Type, Func<object, object, bool>>
    {
        { typeof(string), (x, y) => !string.Equals(x?.ToString(), y?.ToString(), StringComparison.InvariantCultureIgnoreCase) }
    };

    var formatters = new Dictionary<Type, Func<object, object>>()
    {
        { typeof(Enum), x => ((Enum)x)?.FormatEnum() }, // doesn't work
        { typeof(DateTime?), x => ((DateTime?)x)?.ToShortDateString() },
        { typeof(string), x => x.ToString().ToLower() },
    };

    foreach (var propertyToCompare in propertiesToCompare)
    {
        var prop1Value = propertyToCompare.GetValue(noteHistory1);
        var prop2Value = propertyToCompare.GetValue(noteHistory2);

        Func<object, object, bool> comparer;
        comparers.TryGetValue(propertyToCompare.PropertyType, out comparer);

        var hasChanged = comparer?.Invoke(prop1Value, prop2Value) ?? prop1Value != prop2Value;

        if (hasChanged)
        {
            Func<object, object> formatter;
            formatters.TryGetValue(propertyToCompare.PropertyType, out formatter);

            var formattedValue = formatter?.Invoke(noteHistory2);
            // TODO: display the value
        }
    }
}




mardi 5 décembre 2023

Cannot always get reflect info for func signature in Go

I have this which returns a string representation of a function signature:

func getFuncSignature(v interface{}) string {

    funcValue := reflect.ValueOf(v)
    funcType := funcValue.Type()
    name := funcType.Name()

    // Function signature
    var params []string
    for i := 0; i < funcType.NumIn(); i++ {
        nm := funcType.In(i).Name()
        if strings.TrimSpace(nm) == "" {
            nm = "<unk>"    // <<<<<<<<<<<<<<<<<<<<< HERE 1
        }
        params = append(params, nm)
    }

    var returns []string
    for i := 0; i < funcType.NumOut(); i++ {
        nm := funcType.Out(i).Name()
        if strings.TrimSpace(nm) == "" {
            nm = "<unk>"  // <<<<<<<<<<<<<<<<<<<<<< HERE 2
        }
        returns = append(returns, nm)
    }

    paramsStr := strings.Join(params, ", ")
    returnsStr := strings.Join(returns, ", ")

    if name != "" {
        return fmt.Sprintf("(func %s(%s) => (%s))", name, paramsStr, returnsStr)
    }

    return fmt.Sprintf("(func(%s) => (%s))", paramsStr, returnsStr)
}

however, some parameter and return types return empty strings, so I put in "" which isn't really ideal. Is there some way to retrieve the names/types of the input and return values?





lundi 4 décembre 2023

C#: how to check if a property type is string? (nullable string) or string [duplicate]

giving this code:

var t = typeof(Sample);
var p1 = t.GetProperty(nameof(Sample.Str1));
var p2 = t.GetProperty(nameof(Sample.Str2));

Console.WriteLine(p1!.PropertyType == p2!.PropertyType);

public class Sample
{
    public string Str1 { get; set; } = default!;
    public string? Str2 { get; set; }
}

in a dotnet project with <Nullable>enable</Nullable> in csproj it prints "True"

but using int instead of string it shows "False".

Is there any way to know if the propertyType is the source code is "string" or "string?" ?





C# Merge Null Or Not Existing Properties and its properties from strongly typed Model after deserialization

The problem is I have a config which I can update using my Model, I deploy new update and user won't get updated configuration file, so it will cause null reference exception, etc. Now I just set default values or use new() to make it at least similar to latest configuration value, but its a hell, I need to always support it, what if I'll or someone else even forget to merge null properties?

I have 3 different config files (YAML, XML, JSON),but with single one model. I want it to merge all null properties, arrays of the arrays, not existing items of the array from default config into existing config, properties of the properties, etc, however I want to save all existing things from the deserialized config.

My Model:

[Serializable]
public class GuardSettings
{
    public GuardSettings()
    {
    }

    public UnturnedGuardOptions UnturnedGuardOptions { get; set; }
    public UnturnedIntegrityVault UnturnedIntegrityVault { get; set; }
    public LocalTimeOptions LocalTimeOptions { get; set; }
    [YamlIgnore, XmlIgnore] public VanillaOptions VanillaOptions { get; set; }
    public LogReport LogReport { get; set; }
    public DiscordWebhookLaunch DiscordWebhookLaunch { get; set; }
    public DiscordWebhookReject DiscordWebhookReject { get; set; }
    public DiscordWebhookAccept DiscordWebhookAccept { get; set; }
    public DiscordWebhookCancel DiscordWebhookCancel { get; set; }
    public DiscordWebhookReport DiscordWebhookReport { get; set; }
    public DiscordWebhookBans DiscordWebhookBans { get; set; }
    public DiscordWebhookUnbans DiscordWebhookUnbans { get; set; }
    public DatabaseOptions DatabaseOptions { get; set; }
    public UnturnedPanelIntegration UnturnedPanelIntegration { get; set; }
    public BanOptions BanOptions { get; set; }
    public PlayerWhitelist PlayerWhitelist { get; set; }
    public HWIDHistory HWIDHistory { get; set; }
    [YamlMember(Alias = "tpsOptions")] public TPSOptions TPSOptions { get; set; }
    public FeaturesOptions FeaturesOptions { get; set; }
    public Feature[] Features { get; set; }
}

[Serializable]
public class Feature
{
    public Feature()
    {
    }

    public string Name { get; set; } = string.Empty;
    public bool Enabled { get; set; }
    public Argument[] Arguments { get; set; } = Array.Empty<Argument>();
}

[Serializable]
public class Argument
{
    public Argument()
    {
    }

    public string? Name { get; set; }
    public object? Value { get; set; }
}

This is how I see it, I have default settings just using my custom factory that configures default config with all properties (in a proper way), and the existing settings is a deserialized config from file (YAML, XML or JSON).

private static GuardSettings Merge(GuardSettings defaultSettings, GuardSettings existingSettings)

After that I want to serialize the Merged config to let configuration being updated, so user can change newest properties and be up to date with changes in the config!

I tried to use Reflection and AutoMapper, but with no success.





dimanche 3 décembre 2023

Using unity game service cloud save - how to retrieve multiple keys at once and load them to appropriate variables

I am using unity game service cloud save to backup important player data. This works great when I use it to save variable(s). However, I cannot figure out how to load variables without hardcoding each possible combination. I am confident there is something simple I am missing, but I do not know what it is, and the AI suggestions I get are off, likely because I am asking the wrong questions.

Please help?

Working function to load multiple variables to the cloud:

    private async Task<string> CloudSaveMultipleValues(Dictionary<string, object> valuesToSave)
{
    try
    {
        await CloudSaveService.Instance.Data.Player.SaveAsync(valuesToSave);

        Debug.Log($"Successfully saved data to cloud");
    }
    catch (CloudSaveValidationException e)
    {
        Debug.LogError(e);
    }
    catch (CloudSaveRateLimitedException e)
    {
        Debug.LogError(e);
    }
    catch (CloudSaveException e)
    {
        Debug.LogError(e);
    }

    return null;
}

Example of the 'valuesToSave input:

allStats_dict = new Dictionary<string, object>
{
    { nameof(gm.stats_CloudSave.gameVersion), gm.stats_CloudSave.gameVersion },
    { nameof(gm.stats_CloudSave.numsPlaced), gm.stats_CloudSave.numsPlaced },
    { nameof(gm.stats_CloudSave.points), gm.stats_CloudSave.points },
    { nameof(gm.stats_CloudSave.gamesPlayed), gm.stats_CloudSave.gamesPlayed },
    { nameof(gm.stats_CloudSave.dateOfFirstGame), gm.stats_CloudSave.dateOfFirstGame }
};

The loading function I am trying to get to work:

private async Task<string> RetrieveSpecificCloudData(HashSet<string> keys)
    {
        try
        {
            var results = await CloudSaveService.Instance.Data.Player.LoadAsync(keys);

            //this confirms I got all the keys I asked for
            Debug.Log($"{results.Count} elements loaded!");

            //this confirms the values are correct
            Debug.Log($"numsPlaced: {results["numsPlaced"].Value.GetAs<int>()}");
            Debug.Log($"dateOfFirstGame: {results["dateOfFirstGame"].Value.GetAs<DateTime>()}");

            //what I cannot figure out is how to assign the value of each key to the appropriate variable???
            //I can hardcode it like this, which works, but there has to be a better way so I do not have to hardcode so many different scenarios... (one for each combination of keys)
            gm.stats_CloudSave.numsPlaced = results["numsPlaced"].Value.GetAs<int>();

            //I need something here that loops through the input (keys), and assigns the value of each key to the appropriate variable. AI tools suggested using reflection, but I can't get it to work...
        }
        catch (CloudSaveValidationException e)
        {
            Debug.LogError(e);
        }
        catch (CloudSaveRateLimitedException e)
        {
            Debug.LogError(e);
        }
        catch (CloudSaveException e)
        {
            Debug.LogError(e);
        }

        return null;
    }

Example of the 'keys' input Hashset:

HashSet<string> keys = new HashSet<string>
    { nameof(gm.stats_CloudSave.numsPlaced),
      nameof(gm.stats_CloudSave.dateOfFirstGame)
    };




samedi 2 décembre 2023

Typescript satisfies condition with Record and generics does not work

This question is related to my other one How to store "types" and instantiate them dynamically in typescript.

Now I'm trying to add generics and abstract base classes into the mix. VsCode complains about everything. And I've tried to tweak the satisfies condition, but nothing works.

Handlers records

    private handlers = {
    [types.CommandType.Click]: handlers.ClickCommandHandler,
    [types.CommandType.Navigate]: handlers.NavigateCommandHandler,
    [types.CommandType.Type]: handlers.TypeCommandHandler,
    [types.CommandType.NewTab]: handlers.NewTabCommandHandler,
    [types.CommandType.AnalyzePage]: handlers.AnalyzePageCommandHandler,
    [types.CommandType.Connect]: handlers.ConnectCommandHandler,
} satisfies Record<types.CommandType, (new <TCommad extends types.AutomationCommand, TResponse>(pageAwaiter: Awaiter, tabs: Map<number, TabSession>) => CommandHandler<TCommad, TResponse>)>;

Error:

Type of computed property's value is 'typeof ClickCommandHandler', which is not assignable to type 'new <TCommad extends AutomationCommand, TResponse>(pageAwaiter: Awaiter, tabs: Map<number, TabSession>) => CommandHandler<TCommad, TResponse>'.
  Types of construct signatures are incompatible.
    Type 'new (pageAwaiter: Awaiter, tabs: Map<number, TabSession>) => ClickCommandHandler' is not assignable to type 'new <TCommad extends AutomationCommand, TResponse>(pageAwaiter: Awaiter, tabs: Map<number, TabSession>) => CommandHandler<TCommad, TResponse>'.
      Construct signature return types 'ClickCommandHandler' and 'CommandHandler<TCommad, TResponse>' are incompatible.
        The types of 'handle' are incompatible between these types.
          Type '(command: ClickCommand) => Promise<AutomationResponse<boolean>>' is not assignable to type '(command: TCommad) => Promise<AutomationResponse<TResponse>>'.
            Types of parameters 'command' and 'command' are incompatible.
              Type 'TCommad' is not assignable to type 'ClickCommand'.
                Type 'AutomationCommand' is missing the following properties from type 'ClickCommand': tabId, selector

CommandHandler, ClickCommandHandler and TabCommandHandler:

export interface CommandHandler<TCommand extends AutomationCommand, TResponse> {
    handle(command: TCommand): Promise<AutomationResponse<TResponse>>;
}
class ClickCommandHandler extends TabCommandHandler<ClickCommand, boolean>
abstract class TabCommandHandler<TCommand extends TabAutomationCommand, TResponse> implements CommandHandler<TCommand, TResponse>

Here's the smallest reproducible Typescript Playground.

This should be simple, what am I missing?





vendredi 1 décembre 2023

c# Enumerable Reflection GetMethod not found method [duplicate]

I have code for getting method Select for Enumerable.

var selectMethod = typeof(Enumerable).GetMethod("Select", BindingFlags.Public | BindingFlags.Static, [typeof(IEnumerable<>), typeof(Func<,>)]);

I getting null. But when i debbugging in source code was method as candidate with exact types as IEnumerable and Func but they were slightly different like they don't have AssemblyQualifiedName and some other fields, it was RuntimeType or something as i know, and as result type which getting in source code is not assignable from type what in method parameters.

I tried other types, but it alsways null.

typeof(IEnumerable<>)
typeof(IEnumerable)
typeof(Enumerable)

I can get method like that and it working.

var selectMethod = typeof(Enumerable).GetMethods()
            .Where(m => m.Name == nameof(Enumerable.Select))
            .First(m => m.GetParameters().Length == 2)
            .MakeGenericMethod(elementType, elementType);

But i interested why GetMethod not working in that case