8
  • タイプのインスタンスをフィールドに格納できないようにタイプ (またはさらに良い場合はインターフェイス) をマークする方法はありますか (および と同様の方法TypedReferenceArgIterator)。
  • 同様に、インスタンスが匿名メソッドを介して渡されるのを防ぐ方法はありますか?また、一般に、上記の 2 つのタイプの動作を模倣する方法はありますか?
  • これは ILDasm を介して、またはより一般的には IL 編集を介して行うことができますか? UnconstrainedMelodyは、コンパイルされたアセンブリのバイナリ編集によって通常は得られない結果を達成するため、同じアプローチで特定の型 (または、より適切には、抽象型またはマーカー インターフェイス) を「マーク」する方法があるかもしれません。

エラーCS0610のドキュメントには次のように記載されているため、コンパイラにハードコードされているとは思えません。

フィールドまたはプロパティとして使用できないタイプがいくつかあります。これらのタイプには...

私の意見では、これらのような型のセットを拡張できることを示唆していますが、間違っている可能性があります。

私はSOについて少し検索しましたが、プログラムでコンパイラエラーをスローすることはできないことを理解していますが、特定の「特別な」タイプの動作を複製できないことを示すソースは見つかりませんでした.

質問の大部分が学術的なものであっても、回答にはいくつかの用途がある可能性があります。たとえば、特定のオブジェクトの有効期間が、それを作成するメソッド ブロックに制限されていることを確認すると便利な場合があります。

EDIT: RuntimeArgumentHandleもう1つの(言及されていない)保存不可能なタイプです。

EDIT 2:それが何らかの用途に使用できる場合、コンパイラだけでなく、CLRもこれらのタイプを別の方法で扱うようです(タイプが他のタイプとまったく変わらないと仮定しています)。たとえば、次のプログラムは、関連をスローしTypeLoadExceptionますTypedReference*。短くするために調整しましたが、必要に応じて回避できます。たとえば、ポインターの型を変更してもvoid*、例外はスローされません。

using System;

unsafe static class Program
{
    static TypedReference* _tr;

    static void Main(string[] args)
    {
        _tr = (TypedReference*) IntPtr.Zero;
    }
}
4

2 に答える 2

5

わかった。これは完全な分析ではありませんが、ILをいじっても、これができるかどうかを判断するには十分な分析だと思います。私が知る限り、これはできません。

また、dotPeekを使用して逆コンパイルされたバージョンを見ると、属性ごとに、またはその他の方法で、その特定のタイプ/その中の特定のタイプについて特別なことは何もわかりませんでした。

namespace System
{
  /// <summary>
  /// Describes objects that contain both a managed pointer to a location and a runtime representation of the type that may be stored at that location.
  /// </summary>
  /// <filterpriority>2</filterpriority>
  [ComVisible(true)]
  [CLSCompliant(false)]
  public struct TypedReference
  {

それで、System.Reflection.Emitを使用してそのようなクラスを作成してみました。

namespace NonStorableTest
{
    //public class Invalid
    //{
    //    public TypedReference i;
    //}

    class Program
    {
        static void Main(string[] args)
        {
            AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("EmitNonStorable"),
                                                                                       AssemblyBuilderAccess.RunAndSave);

            ModuleBuilder moduleBuilder = asmBuilder.DefineDynamicModule("EmitNonStorable", "EmitNonStorable.dll");

            TypeBuilder invalidBuilder = moduleBuilder.DefineType("EmitNonStorable.Invalid",
                                                                  TypeAttributes.Class | TypeAttributes.Public);

            ConstructorBuilder constructorBuilder = invalidBuilder.DefineDefaultConstructor(MethodAttributes.Public);

            FieldBuilder fieldI = invalidBuilder.DefineField("i", typeof (TypedReference), FieldAttributes.Public);

            invalidBuilder.CreateType();
            asmBuilder.Save("EmitNonStorable.dll");

            Console.ReadLine();
        }
    }
}

つまり、実行すると、TypeLoadExceptionがスローされ、そのスタックトレースはSystem.Reflection.Emit.TypeBuilder.TermCreateClassを指します。それで、私はその後、逆コンパイラーを使用して、これを取得しました。

[SuppressUnmanagedCodeSecurity]
[SecurityCritical]
[DllImport("QCall", CharSet = CharSet.Unicode)]
private static void TermCreateClass(RuntimeModule module, int tk, ObjectHandleOnStack type);

CLRの管理されていない部分を指しています。この時点で、負けないように、CLRの参照バージョンの共有ソースを掘り下げました。この答えがすべての合理的な使用を超えて肥大化するのを避けるために、私が行ったすべてのトレースを実行することはしませんが、最終的には、MethodTableBuilder::SetupMethodTable2関数の\clr\ src \ vm \ class.cppになります(これは、フィールド記述子を設定しているようにも見えます)、次の行があります。

// Mark the special types that have embeded stack poitners in them
                        if (strcmp(name, "ArgIterator") == 0 || strcmp(name, "RuntimeArgumentHandle") == 0)
                            pClass->SetContainsStackPtr();

if (pMT->GetInternalCorElementType() == ELEMENT_TYPE_TYPEDBYREF)
                            pClass->SetContainsStackPtr();

この後者は、\ src \ inc \ cortypeinfo.hにある情報に関連しているため、次のようになります。

// This describes information about the COM+ primitive types

// TYPEINFO(enumName,               className,          size,           gcType,         isArray,isPrim, isFloat,isModifier)

[...]

TYPEINFO(ELEMENT_TYPE_TYPEDBYREF,  "System", "TypedReference",2*sizeof(void*), TYPE_GC_BYREF, false,  false,  false,  false)

(実際には、そのリストで唯一のELEMENT_TYPE_TYPEDBYREFタイプです。)

次に、ContainsStackPtr()は、フィールドなど、特定のタイプが使用されないようにするために、さまざまな場所で使用されます-\ src \ vm \ class.cp、MethodTableBuilder :: InitializeFieldDescs()から:

// If it is an illegal type, say so
if (pByValueClass->ContainsStackPtr())
{
    BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}

とにかく:長い、長い、長い話を短くするために、この方法で保存できないタイプが効果的にCLRにハードコードされている場合があります。したがって、リストを変更したい場合、タイプを保存不可としてフラグを立てるIL手段を提供する場合、Monoまたは共有ソースCLRを使用して、独自のバージョンをスピンオフする必要があります。

于 2013-01-19T16:16:21.363 に答える
2

これらのタイプが何らかの形で特別であることを示すものは IL に見つかりませんでした。int/Int32を internal typeint32に変換したり、構造体に関連する多くのことを行うなど、コンパイラには非常に多くの特殊なケースがあることを私は知っていNullableます。これらのタイプも特殊なケースであると強く思います。

考えられる解決策はRoslynで、このような制約を作成できると思います。

于 2013-01-16T16:26:50.100 に答える