内部输入类的 Microsoft.ML C# 属性必须是公共的,否则错误

问题描述 投票:0回答:1

我正在开发一个 ML.NET 项目,我将数据加载到 IDataView 中,应用 ONNX 模型转换,然后访问转换后的输出。 我的输入类与我的输出类和模型类位于同一程序集中。我将我的输入类标记为

internal
。我将我的输入类属性标记为
internal
并且出现错误。当我将属性标记为
public
(类仍然是
internal
)时,它就可以工作了。

我认为,如果类被标记为

internal
,如果属性/方法是internal
public
,则不会有
没有区别
(引用:“内部类的公共成员实际上是内部的。”) 。显然确实如此。

这是一个最小的可重复示例:

using Microsoft.ML;
using Microsoft.ML.Data;

namespace ConsoleApp1
{
    internal class InputData
    {
        [ColumnName("onnx::Concat_0")]
        [VectorType(342)]
        // public float[] XA1 { get; set; } = new float[342];  // Works

        internal float[] XA1 { get; set; } = new float[342]; // Fails
    }

    public class OutputData
    {
        [ColumnName("onnx::Concat_0")]
        [VectorType(342)]
        public float[] XA1 { get; set; }
    }

    class Program
    {
        static void Main()
        {
            var mlContext = new MLContext();

            var inputData = new[]
            {
                new InputData { XA1 = new float[342] }
            };

            IDataView inputDataView = mlContext.Data.LoadFromEnumerable(inputData);

            // Simulate an ONNX transformation pipeline
            var pipeline = mlContext.Transforms.CopyColumns(
                "onnx::Concat_0",
                "onnx::Concat_0"
            );

            var model = pipeline.Fit(inputDataView);
            IDataView transformedData = model.Transform(inputDataView);

            var transformed = mlContext.Data.CreateEnumerable<OutputData>(transformedData, reuseRowObject: false);
            foreach (var item in transformed)
            {
                Console.WriteLine($"XA1 Length: {item.XA1.Length}");
            }
        }
    }
}

如果有帮助:

  • 我正在使用
    net8.0
  • 我正在使用
    JetBrains Rider 2024.2.6
c# access-modifiers
1个回答
0
投票

事实上,在“正常”上下文中,内部成员和公共成员之间没有区别,但是在反射上下文中,存在区别,这就是导致此行为的原因。

如果您在 ML.NET 代码中导航1,当您执行

mlContext.Data.LoadFromEnumerable(inputData);
时,ML.NET 会在
SchemaDefinition.GetMemberInfos(Type, Direction)
中构建有关类的字段和属性的信息,以下是其中的相关代码:

var fieldInfos = userType.GetFields(BindingFlags.Public | BindingFlags.Instance);
var propertyInfos = userType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
    ...

在这里您可以看到他们使用

BindingFlags.Public
,这意味着仅采用公共属性/字段。如果他们想要处理内部(或者更确切地说,所有非公共)属性/字段,他们会使用
BindingFlags.NonPublic
(或者
BindingFlags.Public | BindingFlags.NonPublic
,如果他们想要公共和非公共)。

这种基于反射的操作通常只关心公共成员,因为正如您链接的问题的“接受的答案”所说,非公共成员是实现细节。 当您执行

pipeline.Fit(inputDataView)

时,它会查找带有

[ColumnName("onnx::Concat_0")]
的属性,但由于它没有处理您的内部属性,因此它找不到任何属性并抛出该异常。


1 从调用 LoadFromEnumerable 到进行反射的代码的整个路径是:



  • DataOperationsCatalog.LoadFromEnumerable<TRow>(IEnumerable<TRow>, SchemaDefinition)
    
    
  • DataViewConstructionUtils.CreateFromEnumerable<TRow>(IHostEnvironment, IEnumerable<TRow>, SchemaDefinition)
    
    
  • InternalSchemaDefinition.Create(Type, SchemaDefinition.Direction)
    
    
  • SchemaDefinition.Create(Type, Direction)
    
    
  • SchemaDefinition.GetMemberInfos(Type, Direction)
    
    
© www.soinside.com 2019 - 2024. All rights reserved.