Skip to content

Support for read-only properties #640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Microsoft.ML.Api/DataViewConstructionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static IDataView CreateFromList<TRow>(IHostEnvironment env, IList<TRow> d
env.AssertValue(data);
env.AssertValueOrNull(schemaDefinition);
var internalSchemaDefn = schemaDefinition == null
? InternalSchemaDefinition.Create(typeof(TRow))
? InternalSchemaDefinition.Create(typeof(TRow), SchemaDefinition.Direction.Read)
: InternalSchemaDefinition.Create(typeof(TRow), schemaDefinition);
return new ListDataView<TRow>(env, data, internalSchemaDefn);
}
Expand All @@ -37,7 +37,7 @@ public static StreamingDataView<TRow> CreateFromEnumerable<TRow>(IHostEnvironmen
env.AssertValue(data);
env.AssertValueOrNull(schemaDefinition);
var internalSchemaDefn = schemaDefinition == null
? InternalSchemaDefinition.Create(typeof(TRow))
? InternalSchemaDefinition.Create(typeof(TRow), SchemaDefinition.Direction.Read)
: InternalSchemaDefinition.Create(typeof(TRow), schemaDefinition);
return new StreamingDataView<TRow>(env, data, internalSchemaDefn);
}
Expand Down
31 changes: 19 additions & 12 deletions src/Microsoft.ML.Api/InternalSchemaDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Linq;
using System.Reflection;
using Microsoft.ML.Runtime.Data;
using Microsoft.ML.Runtime.Internal.Utilities;
using static Microsoft.ML.Runtime.Api.SchemaDefinition;

namespace Microsoft.ML.Runtime.Api
{
Expand All @@ -30,23 +30,27 @@ public class Column
public readonly Delegate Generator;
private readonly Dictionary<string, MetadataInfo> _metadata;
public Dictionary<string, MetadataInfo> Metadata { get { return _metadata; } }
public Type ComputedReturnType {get { return ReturnParameterInfo.ParameterType.GetElementType(); }}
public Type ComputedReturnType { get { return ReturnParameterInfo.ParameterType.GetElementType(); } }
public Type FieldOrPropertyType => (MemberInfo is FieldInfo) ? (MemberInfo as FieldInfo).FieldType : (MemberInfo as PropertyInfo).PropertyType;
public Type OutputType => IsComputed ? ComputedReturnType : FieldOrPropertyType;

public Column(string columnName, ColumnType columnType, MemberInfo memberInfo) :
this(columnName, columnType, memberInfo, null, null) { }
this(columnName, columnType, memberInfo, null, null)
{ }

public Column(string columnName, ColumnType columnType, MemberInfo memberInfo,
Dictionary<string, MetadataInfo> metadataInfos) :
this(columnName, columnType, memberInfo, null, metadataInfos) { }
this(columnName, columnType, memberInfo, null, metadataInfos)
{ }

public Column(string columnName, ColumnType columnType, Delegate generator) :
this(columnName, columnType, null, generator, null) { }
this(columnName, columnType, null, generator, null)
{ }

public Column(string columnName, ColumnType columnType, Delegate generator,
Dictionary<string, MetadataInfo> metadataInfos) :
this(columnName, columnType, null, generator, metadataInfos) { }
this(columnName, columnType, null, generator, metadataInfos)
{ }

private Column(string columnName, ColumnType columnType, MemberInfo memberInfo = null,
Delegate generator = null, Dictionary<string, MetadataInfo> metadataInfos = null)
Expand Down Expand Up @@ -204,13 +208,16 @@ public static void GetVectorAndKind(Type rawType, string name, out bool isVector
throw Contracts.ExceptParam(nameof(rawType), "Could not determine an IDataView type for member {0}", name);
}

public static InternalSchemaDefinition Create(Type userType, SchemaDefinition userSchemaDefinition = null)
public static InternalSchemaDefinition Create(Type userType, Direction direction)
{
Contracts.AssertValue(userType);
Contracts.AssertValueOrNull(userSchemaDefinition);
var userSchemaDefinition = SchemaDefinition.Create(userType, direction);
return Create(userType, userSchemaDefinition);
}

if (userSchemaDefinition == null)
userSchemaDefinition = SchemaDefinition.Create(userType);
public static InternalSchemaDefinition Create(Type userType, SchemaDefinition userSchemaDefinition)
{
Contracts.AssertValue(userType);
Contracts.AssertValue(userSchemaDefinition);

Column[] dstCols = new Column[userSchemaDefinition.Count];

Expand Down Expand Up @@ -238,7 +245,7 @@ public static InternalSchemaDefinition Create(Type userType, SchemaDefinition us

//Clause to handle the field that may be used to expose the cursor channel.
//This field does not need a column.
if ( (memberInfo is FieldInfo && (memberInfo as FieldInfo).FieldType == typeof(IChannel)) ||
if ((memberInfo is FieldInfo && (memberInfo as FieldInfo).FieldType == typeof(IChannel)) ||
(memberInfo is PropertyInfo && (memberInfo as PropertyInfo).PropertyType == typeof(IChannel)))
continue;

Expand Down
4 changes: 3 additions & 1 deletion src/Microsoft.ML.Api/MapTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ public MapTransform(IHostEnvironment env, IDataView source, Action<TSrc, TDst> m
_mapAction = mapAction;
_inputSchemaDefinition = inputSchemaDefinition;
_typedSource = TypedCursorable<TSrc>.Create(Host, Source, false, inputSchemaDefinition);
var outSchema = outputSchemaDefinition == null
? InternalSchemaDefinition.Create(typeof(TDst), SchemaDefinition.Direction.Write)
: InternalSchemaDefinition.Create(typeof(TDst), outputSchemaDefinition);

var outSchema = InternalSchemaDefinition.Create(typeof(TDst), outputSchemaDefinition);
_schema = MergedSchema.Create(_source.Schema, outSchema);
}

Expand Down
15 changes: 13 additions & 2 deletions src/Microsoft.ML.Api/SchemaDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,21 @@ public Column this[string columnName]
}
}

[Flags]
public enum Direction
{
Read = 1,
Write = 2,
Both = Read | Write
}

/// <summary>
/// Create a schema definition by enumerating all public fields of the given type.
/// </summary>
/// <param name="userType">The type to base the schema on.</param>
/// <param name="direction">Accept fields and properties based on their direction.</param>
/// <returns>The generated schema definition.</returns>
public static SchemaDefinition Create(Type userType)
public static SchemaDefinition Create(Type userType, Direction direction = Direction.Both)
{
// REVIEW: This will have to be updated whenever we start
// supporting properties and not just fields.
Expand All @@ -345,7 +354,9 @@ public static SchemaDefinition Create(Type userType)
var propertyInfos =
userType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.CanRead && x.CanWrite && x.GetGetMethod() != null && x.GetSetMethod() != null && x.GetIndexParameters().Length == 0);
.Where(x => (((direction & Direction.Read) == Direction.Read && (x.CanRead && x.GetGetMethod() != null)) ||
((direction & Direction.Write) == Direction.Write && (x.CanWrite && x.GetSetMethod() != null))) &&
x.GetIndexParameters().Length == 0);

var memberInfos = (fieldInfos as IEnumerable<MemberInfo>).Concat(propertyInfos).ToArray();

Expand Down
7 changes: 5 additions & 2 deletions src/Microsoft.ML.Api/TypedCursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,11 @@ public static TypedCursorable<TRow> Create(IHostEnvironment env, IDataView data,
env.AssertValue(data);
env.AssertValueOrNull(schemaDefinition);

var intSchemaDefn = InternalSchemaDefinition.Create(typeof(TRow), schemaDefinition);
return new TypedCursorable<TRow>(env, data, ignoreMissingColumns, intSchemaDefn);
var outSchema = schemaDefinition == null
? InternalSchemaDefinition.Create(typeof(TRow), SchemaDefinition.Direction.Write)
: InternalSchemaDefinition.Create(typeof(TRow), schemaDefinition);

return new TypedCursorable<TRow>(env, data, ignoreMissingColumns, outSchema);
}

private abstract class TypedRowBase : IRow<TRow>
Expand Down
58 changes: 51 additions & 7 deletions test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -678,24 +678,27 @@ public abstract class BaseClassWithInheritedProperties
}


public class ClassWithPrivateFieldsAndProperties
public class ClassWithPrivateFieldsAndProperties
{
public ClassWithPrivateFieldsAndProperties() { seq++; _unusedStaticField++; _unusedPrivateField1 = 100; }
public ClassWithPrivateFieldsAndProperties() { seq++; _unusedStaticField++; _unusedPrivateField1 = 100; }
static public int seq;
static public int _unusedStaticField;
private int _unusedPrivateField1;
private string _fString;

// This property is ignored because it has no setter
[NoColumn]
// This property can be used as source for DataView, but not casting from dataview to collection
private int UnusedReadOnlyProperty { get { return _unusedPrivateField1; } }

// This property is ignored because it is private
private int UnusedPrivateProperty { get { return _unusedPrivateField1; } set { _unusedPrivateField1 = value; } }

// This property is ignored because it has a private setter

[NoColumn]
// This property can be used as source for DataView, but not casting from dataview to collection
public int UnusedPropertyWithPrivateSetter { get { return _unusedPrivateField1; } private set { _unusedPrivateField1 = value; } }

// This property is ignored because it has a private getter

[NoColumn]
// This property can be used as receptacle for dataview, but not as source for dataview.
public int UnusedPropertyWithPrivateGetter { private get { return _unusedPrivateField1; } set { _unusedPrivateField1 = value; } }

public string StringProp { get { return _fString; } set { _fString = value; } }
Expand Down Expand Up @@ -978,5 +981,46 @@ public void RoundTripConversionWithArrayPropertiess()
Assert.True(!enumeratorNullable.MoveNext() && !originalNullalbleEnumerator.MoveNext());
}
}

class ClassWithGetter
{
private DateTime _dateTime = DateTime.Now;
public float Day { get { return _dateTime.Day; } }
public int Hour { get { return _dateTime.Hour; } }
}

class ClassWithSetter
{
public float Day { private get; set; }
public int Hour { private get; set; }

[NoColumn]
public float GetDay => Day;
[NoColumn]
public int GetHour => Hour;
}

[Fact]
public void PrivateGetSetProperties()
{
var data = new List<ClassWithGetter>()
{
new ClassWithGetter(),
new ClassWithGetter(),
new ClassWithGetter()
};

using (var env = new TlcEnvironment())
{
var dataView = ComponentCreation.CreateDataView(env, data);
var enumeratorSimple = dataView.AsEnumerable<ClassWithSetter>(env, false).GetEnumerator();
var originalEnumerator = data.GetEnumerator();
while (enumeratorSimple.MoveNext() && originalEnumerator.MoveNext())
{
Assert.True(enumeratorSimple.Current.GetDay == originalEnumerator.Current.Day &&
enumeratorSimple.Current.GetHour == originalEnumerator.Current.Hour);
}
}
}
}
}