4

I am using INotifyPropertyChanged to propagate changes across my WinForm app.

In this, I have code that converts between a UNIX epoch integer timestamp and a System DateTime object. It receives a ConvertEventArgs object that contains an object value and the DesiredType to convert to.

When using INotifyPropertyChanged and adding a dynamic binding to a Control, you can provide handlers that feed the DesiredType in the ConvertEventArg object.

I have a base class that I call NotifyBase which includes this static function:

public class NotifyBase {
    public static void UpdateDataBindings(string propertyName
                                            , Control ctl
                                            , object bindingSource
                                            , string bindingPropertyName
                                            , Action<object, ConvertEventArgs> formatFunc = null
                                            , Action<object, ConvertEventArgs> parseFunc = null
                                            , bool clearBefore = true
                                            , bool formattingEnabled = true) {
        var b = new Binding(propertyName, bindingSource, bindingPropertyName, formattingEnabled, DataSourceUpdateMode.OnPropertyChanged);
        if (formatFunc != null) b.Format += (sender, args) => formatFunc(sender,args);
        if (parseFunc != null) b.Format += (sender, args) => parseFunc(sender,args);
        if(clearBefore) ctl.DataBindings.Clear();
        ctl.DataBindings.Add(b)

The base class also implements this:

    public event PropertyChangedEventHandler PropertyChanged;
    protected void SetPropertyField<T>(ref T field, T newValue,
[System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") {
            if (EqualityComparer<T>.Default.Equals(field, newValue)) return;
            field = newValue;
            // Ignoring online conjecture about the need to keep a reference to the event property
            // before invoking it. 
            // 1. this is a single threaded app.
            // 2. documentation clearly says that event handlers should merely be checked for being null.
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

I then initialize my databindings. Here is the example with a Winform DateTimePicker.

NotifyBase.UpdateDataBindings("Value", TimePickerBox, Config, nameof(Config.UnitTimestamp)
                , (o, args) => { NotifyBase.PropertyTypeChange(args, ReportingTimeZoneInfo); }
                , (o, args) => { NotifyBase.PropertyTypeChange(args, ReportingTimeZoneInfo); });

The PropertyTypeChange function essentially takes the ConvertEventArg and feeds it the type that it wants:

Type u = Nullable.GetUnderlyingType(carg.DesiredType);
if (carg.DesiredType == typeof(DateTime) || u == typeof(DateTime)) {
    if (u == null) {
        if (carg.Value is int) {
            carg.Value = epoch.AddSeconds((int) carg.Value);
        }
        else if (carg.Value is DateTime) carg.Value = carg.Value; // do not throw if type is already the expected type.
        else throw new ArgumentOutOfRangeException("Value type conversion to DateTime is not supported.");
    } else {
        DateTime tp;
        if (carg.Value is int) {
            tp = epoch.AddSeconds((int) carg.Value);
        }
        else if (carg.Value is DateTime) tp = (DateTime) carg.Value; // do not throw if type is already the expected type.
        else throw new ArgumentOutOfRangeException("Value type conversion to DateTime is not supported.");
        carg.Value = tp.MakeNullable<DateTime>();
    }
}

Anyway, thought using "is" to do type correspondence was more elegant. But as my watch window shows, it isn't resolving to an int even though it clearly shows the object is an integer:

Watch window

My question:

I thought the is expression was equivalent to object.GetType() == typeof(int). Is this not true?

It doesn't appear to be and I'd like to better understand the conditions under which that's true.

Anyway, in the absence of a solid answer, I'm going to move on and just make everything use GetType().

Using: Visual Studio 2015 Using: Microsoft.Net 4.6.1 Framework.

  • 1
    `x is int` will be true if `x` evaluates to an integer (that is, a value of System.Int32). Start with this given. What is `x` *really* when that code is executed? Sometimes the watch windows can be .. confusing, especially as they may "show" a string (ie. `"1"`) and an integer (ie. `1`) the same due to how they are displayed. Using `carg.Value.GetType()`, eg., should help clear that up. – user2864740 Mar 26 '18 at 21:19
  • 1
    1) Can you output `carg.Value.GetType()` without using the debugger? 2) Please post some sample code which exhibits this behaviour. – CodesInChaos Mar 26 '18 at 21:34
  • 1
    On an unrelated note: Use 64-bit integers for unix timestamps, not 32-bit integers to avoid Y2038. – CodesInChaos Mar 26 '18 at 21:37
  • 1
    [`is` is not equivalent to `GetType()`](https://stackoverflow.com/q/983030/4934172). That being said, from your watch window, your object does seem to be an int! Perhaps you need to post some sample code which can demonstrate this behavior as CodesInChaos has suggested. – 41686d6564 stands w. Palestine Mar 26 '18 at 21:53
  • @ CodesInChaos 32bit timestamp was not my design. It's from an embedded system that I support. The code isn't much more complicated than in my snippet. I'll add additional context. – Jacques Petit Mar 26 '18 at 22:02
  • 3
    just for your info, closing questions (and reopening them) are mostly handled by community users (with enough reputation points), and it was in this case. Adding interesting and precise details, but finishing your edit with some grumbling about "moderators" won't help your question to be voted for reopening. Just saying ;) . – Pac0 Mar 26 '18 at 23:24
  • @ Pac0 .... I probably shouldn't have asked the question anyway. Some problems just require too much rabbit hole work to contextualize. Honestly, I don't need help with the code. I just wanted to know if there was a legal C# reason for an "object type of integer" to fail the "object is integer" expression. – Jacques Petit Mar 26 '18 at 23:36
  • 1
    It is a problem caused by the debugger, not C#. Tools > Options > Debugging > General > tick "Use Managed Compatibility Mode". This enables an older version of the debugging engine, last used in VS2010. The last good one. – Hans Passant Mar 27 '18 at 06:24
  • @"Hans Passant" I can confirm at this point that is is a debugger problem. The debugger inspection is entirely lying about the value of the expression. object is int evaluates to true even though the debugger says it is false. – Jacques Petit Mar 28 '18 at 20:44

0 Answers0