Skip to content

C# 8 Language Features for API-30 Bindings #509

Closed
@jonpryor

Description

@jonpryor

Whenever API-30 comes out, we would like to adopt C#8 features as part of the new binding in the following ways:

  1. Default interface members for added interface & abstract methods
  2. New Java interfaces containing nested types will "remain" nested in the C# binding. Existing nested types will be "unchanged", structurally speaking.

TODO:

Determining newly added members

Within a xamarin-android/src/Mono.Android build, api-merge.exe is used to "merge" the various API XML files together so that we know in which API level a member was added. This information is in the //@merge.SourceFile attribute, e.g.

<field
    name="ACTIVITY_RECOGNITION" …
    merge.SourceFile="..\..\bin\BuildDebug\api\api-29.xml.in"
/>

Structure of existing types is not changed

Consider the android.view.MenuItem and android.view.MenuItem.OnActionExpandListener interfaces: MenuItem.OnActionExpandListener is nested within the MenuItem interface.

Versions of C# prior to C#8 didn't support nesting types within interfaces, so the C#7 binding of these types is:

partial interface IMenuItem {
}
partial interface IMenuItemOnActionExpandListener {
}

This structure will not change for existing types.

Structure of new types will require C#8 features

For types added in API-30, the types will be nested. If the Java declaration is:

package android.example;

public interface Outer {
    public static final int O = 1;
    public interface Nested {
        public static final int N = 2;
    }
}

Then the C# declaration will mirror the Java structure:

namespace Android.Example {
    public partial interface IOuter {
        public const int O = 1;
        public partial interface INested {
            public const int N = 2;
        }
    }
}

Default Interface Members to maintain compatibility

When a new abstract method is added to an abstract class, or a new required (non-default) method is added to a Java interface -- as was done with android.database.Cursor in API-11, API-19, API-23, and API-29 -- then a new default interface method should be emitted which does a throw new AbstractMethodError().

For example, consider ICursor for API-10:

partial interface ICursor {
    // ...
}

API-11 added a new Cursor.getType(int) method. If we had C#8 during API-11, we could have done:

// for API-11
partial interface ICursor {
    // … same as API-10

    // Added in API-11
    int GetType (int columnIndex)
    {
        throw new AbstractMethodError ();
    }
}

All interfaces updated to contain static members

As C#7 didn't support static members, if an interface had any static members, e.g. public static final int fields, then those fields were emitted into a class:

namespace Android.OS {
    public partial interface IParcelable : IJavaObject, IJavaPeerable {
        // …
    }
    public partial class Parcelable {
        [Register ("CONTENTS_FILE_DESCRIPTOR")]
        public const int ContentsFileDescriptor = (int) 1;

        [Register ("PARCELABLE_WRITE_RETURN_VALUE")]
        [Obsolete ("This constant will be removed in the future version. Use Android.OS.ParcelableWriteFlags enum directly instead of this field.")]
        public const Android.OS.ParcelableWriteFlags ParcelableWriteReturnValue = (Android.OS.ParcelableWriteFlags) 1;
    }
}

For compatibility, we cannot remove the Parcelable type. What we can and should do, is add all of those members to the interface as well, and make the Parcelable type [Obsolete(error:true)]

namespace Android.OS {
    public partial interface IParcelable : IJavaObject, IJavaPeerable {
        // …

        [Register ("CONTENTS_FILE_DESCRIPTOR")]
        public const int ContentsFileDescriptor = (int) 1;

        [Register ("PARCELABLE_WRITE_RETURN_VALUE")]
        public const Android.OS.ParcelableWriteFlags ParcelableWriteReturnValue = (Android.OS.ParcelableWriteFlags) 1;
    }
    [Obsolete("Use the IParcelable type", error:true)]
    public partial class Parcelable {
        [Register ("CONTENTS_FILE_DESCRIPTOR")]
        [Obsolete("Use IParcelable.ContentsFileDescriptor", error:true)]
        public const int ContentsFileDescriptor = (int) 1;

        [Register ("PARCELABLE_WRITE_RETURN_VALUE")]
        [Obsolete ("This constant will be removed in the future version. Use Android.OS.ParcelableWriteFlags.ReturnValue instead of this field.")]
        public const Android.OS.ParcelableWriteFlags ParcelableWriteReturnValue = (Android.OS.ParcelableWriteFlags) 1;
    }
}

This will "duplicate" members. We should [Obsolete] the Parcelable (and similar) types in API-30.

New members should only be added to the interface, not the "constants class". The actual constants class, ParcelableConsts, should be removed from the binding for API-30, as it will be Obsolete with error:true for over a year by the time this is expected to happen.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions