【问题标题】:C# Anonymous types cannot be assigned to -- it is read onlyC# 匿名类型不能分配给——它是只读的
【发布时间】:2023-03-26 18:45:02
【问题描述】:

这个code-sn-p有什么问题?

class Program
{
    static void Main(string[] args)
    {
        var obj = new { Name = "A", Price = 3.003 };

        obj.Name = "asdasd";
        obj.Price = 11.00;

        Console.WriteLine("Name = {0}\nPrice = {1}", obj.Name, obj.Price);

        Console.ReadLine();
    }
}

我收到以下错误:

Error   5   Property or indexer 'AnonymousType#1.Name' cannot be assigned to -- it is read only .....\CS_30_features.AnonymousTypes\Program.cs  65  13  CS_30_features.AnonymousTypes
Error   6   Property or indexer 'AnonymousType#1.Price' cannot be assigned to -- it is read only    .....\CS_30_features.AnonymousTypes\Program.cs  66  13  CS_30_features.AnonymousTypes

如何将值重新设置为匿名类型对象?

【问题讨论】:

    标签: c# c#-3.0 anonymous-types


    【解决方案1】:

    C# 中的匿名类型是不可变的,因此没有属性设置方法。您需要使用值创建一个新的匿名类型

    obj = new { Name = "asdasd", Price = 11.00 };
    

    【讨论】:

    • 还有一点需要注意的是,如果新的匿名类型具有相同数量和相同顺序的属性类型,它将与第一个具有相同的内部类型
    • 这可能是一个非常有用的注释,感谢您添加! +1
    【解决方案2】:

    匿名类型是用只读属性创建的。对象构造完成后,您无法分配给它们。

    来自 MSDN 上的Anonymous Types (C# Programming Guide)

    匿名类型提供了一种方便的方法,可以将一组只读属性封装到单个对象中,而无需先显式定义类型。

    【讨论】:

      【解决方案3】:

      匿名类型提供了一种方便的方式来封装一组 只读属性合并为一个 对象而不必先 显式定义一个类型。方式 名称由编译器生成,并且 在源代码中不可用 等级。属性的类型是 由编译器推断。这 以下示例显示了一个匿名的 用两个初始化的类型 名为 Amount 和 Message 的属性。

      http://msdn.microsoft.com/en-us/library/bb397696.aspx

      【讨论】:

        【解决方案4】:

        请改用ExpandoObject,因为它支持在创建对象后更新/添加新属性(自 C# 4 以来一直存在)。

        请注意,使用关键字dynamic(而不是var)声明对象很重要

        using System.Dynamic;
        
        dynamic person = new ExpandoObject();
        person.FirstName = "John";
        person.LastName = "Doe";
        

        【讨论】:

          【解决方案5】:
              /// <summary>
              /// Of 8 bytes.
              /// A structure which represents an:
              /// <see cref="System.Object"/>, <see cref="System.Array"/> or <see cref="System.String"/>.
              /// </summary>
              [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit, Size = 8)]
              public struct Invariant
              {
                  [System.Runtime.InteropServices.FieldOffset(0)]
                  public System.ValueType Value;
          
                  [System.Runtime.InteropServices.FieldOffset(0)]
                  public System.Array Array;
          
                  [System.Runtime.InteropServices.FieldOffset(0)]
                  public object Object;
          
                  [System.Runtime.InteropServices.FieldOffset(0)]
                  public string String;
          
                  /// <summary>
                  /// Used to interpret the address/memory of object which can be thought of as IntPtr*
                  /// Remember to deference the type 1 time to inspect the memory where ref object points to.
                  /// </summary>
                  /// <typeparam name="TType"></typeparam>
                  /// <param name="index"></param>
                  /// <param name="length"></param>
                  /// <returns></returns>
                  public System.Span<TType> GetSpan<TType>(int index, int length) => System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref System.Runtime.CompilerServices.Unsafe.Add(ref System.Runtime.CompilerServices.Unsafe.As<object, TType>(ref Object), index), length);
          
                  /// <summary>
                  /// Get a <see cref="System.IntPtr"/> which points to the address of <see cref="Object"/>
                  /// </summary>
                  /// <returns></returns>
                  public System.IntPtr ToPointer() => GetSpan<System.IntPtr>(0, 1)[0] + System.IntPtr.Size + System.IntPtr.Size; //Syncbloc, Rtti
          
                  /// <summary>
                  /// 
                  /// </summary>
                  /// <returns></returns>
                  public bool IsAligned() => (ulong)ToPointer() % (ulong)System.IntPtr.Size == 0;
          
                  /// <summary>
                  /// Allowing one to set the <see cref="System.Type"/> of <see cref="Object"/>
                  /// </summary>
                  public System.Type Type
                  {
                      set
                      {
                          System.Runtime.InteropServices.Marshal.WriteIntPtr(GetSpan<System.IntPtr>(0, 1)[0], value.TypeHandle.Value);
                          //System.Runtime.CompilerServices.Unsafe.AsRef(System.Runtime.CompilerServices.Unsafe.As<object, System.IntPtr>(ref Object)) = value.TypeHandle.Value;
                          //System.Runtime.CompilerServices.Unsafe.AsRef(GetSpan<System.IntPtr>(0, 1)[0]) = value.TypeHandle.Value;
                      }
                  }
              }
          
              /// <summary>
              /// A single value
              /// Will implicitly convert to <see cref="Invariant"/>
              /// </summary>
              /// <typeparam name="T"></typeparam>
              public ref struct Invariant<T>
              {
                  public static System.RuntimeTypeHandle TypeHandle => typeof(T).TypeHandle;
          
                  public static System.IntPtr ToPointer(ref T t)
                  {
                      System.IntPtr rtti = System.Runtime.CompilerServices.Unsafe.As<T, System.IntPtr>(ref t);
          
                      //rtti = System.Runtime.InteropServices.Marshal.ReadIntPtr(rtti, 0);
          
                      return rtti;
                  }
          
                  /// <summary>
                  /// The value
                  /// </summary>
                  public T Value;
          
                  /// <summary>
                  /// Implicit create <see cref="Invariant"/>
                  /// </summary>
                  /// <param name="src"></param>
          
                  public static implicit operator Invariant(Invariant<T> src) => new Invariant() { Object = src.Value };
          
                  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  public static T Emplace(ref Invariant invariant, bool writeHeader) => Emplace(invariant.ToPointer(), writeHeader);
          
                  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  public static T Emplace(ref Invariant invariant) => Emplace(invariant.ToPointer(), false == System.Type.GetTypeFromHandle(TypeHandle).IsValueType);//Might need to read the void* in the spann which is is different places depending on the runtime.
          
                  /// <summary>
                  /// Emplace the value initializing the Object header and TypeHandle.
                  /// </summary>
                  /// <param name="bufferPtr"></param>
                  /// <returns></returns>
                  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  public static T Emplace(System.IntPtr bufferPtr, bool writeHeader = true)
                  {
                      var dataPointer = bufferPtr + 1;
          
                      //New way
                      if (writeHeader) System.Runtime.CompilerServices.Unsafe.AsRef(in bufferPtr) = System.IntPtr.Zero;
                      System.Runtime.CompilerServices.Unsafe.AsRef(in dataPointer) = typeof(T).TypeHandle.Value;
          
                      //Old way
                      //if (writeHeader) System.Runtime.InteropServices.Marshal.WriteIntPtr(bufferPtr, System.IntPtr.Zero); //Object Header
                      //System.Runtime.InteropServices.Marshal.WriteIntPtr(dataPointer, typeof(T).TypeHandle.Value);
          
                      return System.Runtime.CompilerServices.Unsafe.As<System.IntPtr, T>(ref dataPointer); //Pointer to the method table pointer
                  }
          
                  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  public static T Emplace(ref T t, bool writeHeader = true) => Emplace(System.Runtime.CompilerServices.Unsafe.AsRef(in System.Runtime.CompilerServices.Unsafe.As<T, System.IntPtr>(ref t)), writeHeader);
                  
                  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  public static T Emplace(ref T t) => Emplace(ref t, false == System.Type.GetTypeFromHandle(TypeHandle).IsValueType);
          
                  /// <summary>
                  /// Useful when ref t has been Emplaced
                  /// </summary>
                  /// <param name="expression"></param>
                  /// <param name="t"></param>
                  /// <param name="arguments"></param>
                  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  public static void Construct(System.Linq.Expressions.Expression<System.Action> expression, ref T t, params object[] arguments)
                  {
                      //figure out which constructorInfo
                      System.Linq.Expressions.NewExpression newExpression = expression.Body as System.Linq.Expressions.NewExpression;
          
                      //newExpression.Arguments
                      //DeclaringType should equal typeof(T)
                      //typeof(T).GetConstructor(System.Array.Empty<System.Type>()).Invoke(t, arguments);
                      //don't read the object returned here its on the stack and boxed refer to t
                      if (null == arguments) arguments = System.Array.Empty<object>();
                      newExpression.Constructor.Invoke(t, arguments);
                      //Could get paramters from expression
                      var instantiator = System.Linq.Expressions.Expression.Lambda/*<System.Func<T>>*/(newExpression).Compile();
                      instantiator.Method.Invoke(t, arguments);
                  }
          
                  //[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  //public unsafe static T Alloc(System.Span<byte> bytes) => Alloc((System.IntPtr)System.Runtime.CompilerServices.Unsafe.AsPointer(ref bytes.GetPinnableReference()));
          
                  //Could be exposed from a static Construct<T>(ref T) method but which constructor to invoke?
                  //Could also accept an Expression and use the expression to build a delegate
                  //Alloc(()=>new Whatever(1,2,3,"4"));
                  //When you have the expression the call to the correct MethodInfo (constructor) is already performed via overload resolution.
                  //
                  //private static unsafe class Constructor
                  //{
                  //    private static readonly delegate*<T, void> ConstructorHandle;
          
                  //    static Constructor()
                  //    {
                  //        System.Type type = typeof(T);
          
                  //        System.Reflection.ConstructorInfo constructorInfo = type.GetConstructor(System.Array.Empty<System.Type>());
          
                  //        Constructor.ConstructorHandle = (delegate*<T, void>)constructorInfo.MethodHandle.GetFunctionPointer();
                  //    }
          
                  //    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
                  //    internal static void Invoke(T returnObject)
                  //    {
                  //        Constructor.ConstructorHandle(returnObject);
                  //    }
                  //}
          
                  /// <summary>
                  /// Creates a <see cref="System.Span{T}"/>
                  /// </summary>
                  /// <returns></returns>
                  public System.Span<T> GetSpan() => System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref Value, 1);
          
                  /// <summary>
                  /// Create a <see cref="System.Span{TType}"/>
                  /// </summary>
                  /// <typeparam name="TType"></typeparam>
                  /// <param name="index"></param>
                  /// <param name="length"></param>
                  /// <returns></returns>
                  public System.Span<TType> GetSpan<TType>(int index, int length) => System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref System.Runtime.CompilerServices.Unsafe.Add(ref System.Runtime.CompilerServices.Unsafe.As<T, TType>(ref Value), index), length);
              }
          
          /// <summary>
          /// Provides extensions useful for <see cref="System.Span{T}"/>
          /// </summary>
          public static class SpanExtensions
          {
              /// <summary>
              /// Pointer to the first element
              /// </summary>
              /// <typeparam name="T"></typeparam>
              /// <param name="span"></param>
              /// <returns></returns>
              public static System.IntPtr ToPointer<T>(this System.Span<T> span)
              {
                  //ptr to struct to interior ptr
                  ref T pp = ref span[0];
                  ref byte b = ref System.Runtime.CompilerServices.Unsafe.As<T, byte>(ref pp);
          
                  //Interior pointer layout and size may vary with such
                  //Object header is 24 bytes and 1 more pointer for the interior ptr itself
                  b = ref System.Runtime.CompilerServices.Unsafe.Add(ref b,(System.IntPtr)(-System.IntPtr.Size * 4));
          
                  //from bite to pointer
                  ref System.IntPtr ptrSpan = ref System.Runtime.CompilerServices.Unsafe.As<byte, System.IntPtr>(ref b);
                  return ptrSpan;
              }
          
              public static int ReadLength(ref System.IntPtr ptr) => System.Runtime.InteropServices.Marshal.ReadInt32(ptr, System.IntPtr.Size);
          
              public static System.Span<T> RecoverSpan<T>(System.IntPtr ptr)
              {
                  //Because of reloc on the stack 1 must keep 2 pts, 1 to the length and 1 to the last element
                  var pb = ptr + (System.IntPtr.Size * 4);
                  ref byte reverse = ref System.Runtime.CompilerServices.Unsafe.As<System.IntPtr, byte>(ref pb);
                  ref T pp = ref System.Runtime.CompilerServices.Unsafe.As<byte, T>(ref reverse);
                  System.Span<T> span = System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref pp, System.Runtime.InteropServices.Marshal.ReadInt32(ptr, System.IntPtr.Size));
                  return span;
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多