开发者

C# Anonymous types cannot be assigned to -- it is read only

开发者 https://www.devze.com 2022-12-08 09:43 出处:网络
What is wrong with this code-snippet? class Program { static void Main(string[] args) { var obj = new { Name = \"A\", Price = 3.003 };

What is wrong with this code-snippet?

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();
    }
}

I am getting the following errors:

Error   5   Property or indexer 'AnonymousType#1.Name'开发者_StackOverflow中文版 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

How to re-set values into an anonymous type object?


Anonymous types in C# are immutable and hence do not have property setter methods. You'll need to create a new anonmyous type with the values

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


Anonymous types are created with read-only properties. You can't assign to them after the object construction.

From Anonymous Types (C# Programming Guide) on MSDN:

Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to first explicitly define a type.


Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to first explicitly define a type. The type name is generated by the compiler and is not available at the source code level. The type of the properties is inferred by the compiler. The following example shows an anonymous type being initialized with two properties called Amount and Message.

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


Use ExpandoObject instead since it supports updating/adding new properties after object creation (it has been around since since C# 4).

Note that it is important to declare the object using the keyword dynamic (instead of var)

using System.Dynamic;

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


    /// <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;
    }
0

精彩评论

暂无评论...
验证码 换一张
取 消