Description
Whenever API-30 comes out, we would like to adopt C#8 features as part of the new binding in the following ways:
- Default interface members for added interface & abstract methods
- New Java interfaces containing nested types will "remain" nested in the C# binding. Existing nested types will be "unchanged", structurally speaking.
TODO:
-
Add default methods. (Interface methods that are explicitly marked asdefault
in Java.)-
--lang-features=default-interface-methods
-
[Mono.Android] Generate default interface members for API-30+. android#4353
-
-
Compatibility DIMs for interface breaking changes.(Not Needed)-
Createapi.xml
attribute to signify interface member needs to be turned into a DIM with aAbstractMethodError
implementation. -
Metadata to mark these methods (likely frommerge.SourceFile
)
-
-
Handle types nested inside interfaces-
Properly nest new types (API-30+) inside interfaces-
--lang-features=nested-interface-types
-
-
Preserve old style of unnesting types declared in an interface for pre-API-30.-
unnest
metadata
-
-
[Mono.Android] Allow interfaces to contain nested types starting with API-30 android#4363
-
-
Generate all constants on their interfaces with DIM (this will duplicate all constants).-
--lang-features=interface-constants
-
[Mono.Android] Enable constants on interfaces. android#4372
-
-
Handle existing constants alternatives-
RemoveParcelableConsts
, etc. classes. ([Obsolete (error: true)]
for over a year.) -
MarkParcelable
, etc. classes as[Obsolete]
warning, with a pointer to new location. -
PR [generator] When using interface-constants, remove or obsolete interface alternatives. #600
-
-
Do not generate new[Obsolete]
constants classes for API-30+ api.
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.