-2

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

d00lar
  • 802
  • 7
  • 25
  • 5
    It is not really clear what the actual question is. I would suggest a *minimal reproducible example*. Also, you might want to explain your actual goal. Generics typically do not work well with nullables, and if you need to check the type of your generic, it seem likely that generics is the wrong solution for your problem. – JonasH Oct 21 '22 at 07:42
  • 1
    ok i edited and pushed code example witch fails if i do not cast it this way based on int/int Please check – d00lar Oct 24 '22 at 06:42

1 Answers1

0

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; }
}
Marius
  • 1,420
  • 1
  • 11
  • 19
  • 1
    thanks but if i do TConverter.ChangeType(item.Key) and T is int and item.key is int then i recive exception Int32Converter cannot convert from System.Int32 – d00lar Oct 21 '22 at 11:13
  • @d00lar, well obviously you did something wrong. Debug and check what `item.Key` is. – Marius Oct 23 '22 at 15:57
  • 1
    ok for simple examples this work but for a bit more complex no Can You Please check this repo ? i uploaded it with this particular error https://github.com/d00lar/TconverterFails – d00lar Oct 24 '22 at 06:41
  • @d00lar, I don't see the repo you are referring to. 404 on your link, and your account only shows 2 repos. – Marius Oct 24 '22 at 13:10
  • ok now - sorry it was private by mistake – d00lar Oct 25 '22 at 05:16
  • @d00lar, your testing approach is flawed. Your conversion is happening in the component, and shouldn't. You need to provide the component with `Dictionary`, and not create the dictionary in the component. When testing, do correct testing by implementing the same as the use-case. – Marius Oct 25 '22 at 14:37
  • @d00lar, my answer was updated with a fix to your problem. – Marius Oct 25 '22 at 17:29
  • thanks. i known that i can fix this way. but this way i canot bind to `int?` property of model. wanted to handle both cases. and as it will be used in many places then i wanted simple component calls by just parsing enum as parameter to this IntStrDictMudSelectComponent. and based on this enum take this dictionary from service IN COMPONENT itself so i does need to call this service in 'parrent' component. and pass in list as You did - thats why i did it hardcoded in component itself in this github example. and conversion is in component because of this. – d00lar Oct 26 '22 at 07:33
  • beside of 'design' issue still why this conversion fails and this tconverter fails ? it should not imho? is it .net bug ?. is what i sad realy terrible idea? to take this dict from service based on enum parameter in component itself? if i do test it right? thanks and regards – d00lar Oct 26 '22 at 07:35
  • If you are using an enum, take the enum and create a `List` where the class has a `T Value` and `string Name` properties. It will work like the dictionary. Then you don't cast. You just use `T` (the enum). That's what I was trying to show you. Try and understand the solution and not criticize. – Marius Oct 26 '22 at 11:18
  • i not criticize in any place sorry if You thought so it was not my intention ;) thanks for all! I just wanted to show my point of view and why i chosed to do like this. Yes i known that it can be Tvalue in class but how to get from api list of Tvalue and string name?? api call have no concept of sending T so it known what to return?. list of keyvaluepair is easy?? ps still why this conversion fails and this tconverter fails ? is it .net bug ? – d00lar Oct 26 '22 at 11:52
  • @d00lar, I can guarantee you it's not a bug. An exception like this will only throw if it cannot be cast to the type you are targeting. From the API you will have a given type passed to your client, this type can be sent as the generic type in the component which will require no casting if you only use this type, and return that type in the `ValueChanged` parameter. As a rule of thumb, generics should be done without casting in most cases. The most common cast will be from `string` to `T` or vice-versa. – Marius Oct 26 '22 at 14:42