Search code examples
c#.netblazorblazor-webassembly

is it .net bug ? should i report this? convert T to int or int?


i have component WHERE T can be int or int? with params like

@typeparam T
@inject DictService _dhttp;


<MudAutocomplete T="string" @bind-Value="ValueString" Label="@Label" For="()=>ValueString" SearchFunc="@SearchFunc" 
             ResetValueOnEmptyText="true" CoerceValue="true" 
             OpenIcon="@Icons.Material.Filled.Search" AdornmentColor="Color.Primary"
             @attributes=AllOtherAttributes />




[Parameter]
public Expression<Func<T>>? For { get; set; }

[Parameter]
public string? Label { get; set; }


private T _value = default!;

[Parameter]
public T Value
{
    get => _value;
    set
    {
        if (!Equals(value, _value))
        {
            _value = value;
            if (ValueChanged.HasDelegate) ValueChanged.InvokeAsync(_value);
        }
    }
}

[Parameter]
public EventCallback<T?> ValueChanged { get; set; } 

private string? _valueString ;
public string? ValueString 
{
    get{
        return _valueString!;
    }
    set
    {
        if(!Equals(value, _valueString))
        {
            _valueString = value;
            int? valueInt = _dict!.Values.Where(... some logic to get in val or not)

            if (valueInt is null)
            {
                ValueString = null;

and now this should work for both cases?!? set 0 if int and null if int? ??

this.Value = (T)(object)default!;

but instead of i have to do

              if (typeof(T) == typeof(int))
                   this.Value = (T)(object)0; 
               else
                    this.Value = (T)(object)default!;

            }
            else this.Value = (T)(object)valueInt;           

            if (ValueChanged.HasDelegate)  ValueChanged.InvokeAsync(_value);
        }
    }
}

if i do not do this way then in debug i see that if T is int then (T)(object)default! like crashes? it should set it to 0 !? it throws no error. it not go to next line , it just returns to app without changing value of this.Value witch is int in this case. value stays as it was from previous run

is it a .net blazor bug? or am missing something here ?

Here is some githup repo that shows this issue

https://github.com/d00lar/TconverterFails

in this line

 <MudSelectItem  Value="TConverter.ChangeType<T>(item.Key)">@item.Value</MudSelectItem>

if i do casting based on this int/int? switch then it will wotk fine otherwise throw as in topic


Solution

  • You are overcomplicating your cast.

    Just use default which will set type int to 0, and type int? to null.

    Index.razor:

    @page "/"
    
    
    <MyComponent T="int" />
    <MyComponent T="int?" />
    

    Component:

    @using System.ComponentModel
    @typeparam T
    
    <div>
        <input type="text" @bind-value="@ValueString" />
    
        <span>@(Value == null ? "null" : Value)</span>
    </div>
    
    @code {
        T Value { get; set; }
    
        string _valueString = "";
    
        string? ValueString
        {
            get
            {
                return _valueString!;
            }
            set
            {
                _valueString = value;
    
                if (string.IsNullOrEmpty(value))
                {
                    this.Value = default;
                }
                else this.Value = TConverter.ChangeType<T>(value); ;
    
                Console.WriteLine(_valueString);
            }
        }
    
        public static class TConverter
        {
            public static T ChangeType<T>(object value)
            {
                return (T)ChangeType(typeof(T), value);
            }
    
            public static object ChangeType(Type t, object value)
            {
                TypeConverter tc = TypeDescriptor.GetConverter(t);
                return tc.ConvertFrom(value);
            }
    
            public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
            {
    
                TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
            }
        }
    }
    

    TConverter source: https://stackoverflow.com/a/1833128/2286743

    Fix to provided link:

    I can see in your approach that your understanding of how to implement components needs to be expanded. I suggest you go through the documentation provided by Microsoft, and improve your understanding.

    As a side note, using a dictionary is not the right way of doing it, since the Key in a Dictionary cannot be null. This can however be overcome as mentioned here: Why doesn't Dictionary<TKey, TValue> support null key?

    index.razor

    @page "/"
    @using MyApplication.Shared
    
    <PageTitle>Index</PageTitle>
    
    <IntStrDictMudSelectComponent @bind-Value="@intvar" Data="@Data" For="@(()=>intvar)" />
    
    @code {
        private int intvar = 1;
    
        Dictionary<int, string> Data = new Dictionary<int, string>() {
            { 1, "Value 1" },
            { 2, "Value 2" },
            { 3, "Value 3" },
            { 4, "Value 4" },
            { 5, "Value 5" },
            { 6, "Value 6" }
        };
    }
    

    IntStrDictMudSelectComponent.razor

    @using System.Linq.Expressions
    @typeparam T
    
    <MudSelect T="T" @bind-Value="@Value" For="@For">
        @foreach (var item in Data)
        {
            <MudSelectItem Value="item.Key">@item.Value</MudSelectItem>
        }
    </MudSelect>
    
    
    @code {
        [EditorRequired]
        [Parameter]
        public Expression<Func<T>>? For { get; set; }
    
        T _value = default!;
    
        [EditorRequired]
        [Parameter]
        public T Value
        {
            get => _value;
            set
            {
                if (!Equals(value, _value))
                {
                    _value = value;
                    if (ValueChanged.HasDelegate)
                        ValueChanged.InvokeAsync(_value);
                }
            }
        }
        
        [EditorRequired]
        [Parameter]
        public EventCallback<T> ValueChanged { get; set; }
    
        [EditorRequired]
        [Parameter]
        public Dictionary<T, string> Data { get; set; }
    }