Calling Internal Method using Reflection

T

Travis Parks

Hello:

I have an internal factory class with methods I want to call from
another DLL using reflection. However, trying to do so results in a
MethodAccessException.

I am assuming this is because library vendors don't want people
accessing internal members. Is there a way around this?

In my situation, I have a factory class that generates
IEqualityComparer<T> types at runtime using System.Reflection.Emit.
The comparers can be recursive, meaning that they can rely on other
dynamically generated comparers. In order to do so, they need to call
methods on the factory.

What are my options? My factory has to be internal or public. I'd
rather avoid exposing this class if I can. I am hoping there is some
flag or attribute that tells the runtime to relax.

Thanks,
Travis Parks
 
T

Travis Parks

Travis said:
I have an internal factory class with methods I want to call from
another DLL using reflection. However, trying to do so results in a
MethodAccessException.
I am assuming this is because library vendors don't want people
accessing internal members. Is there a way around this? [...]

You need to be more specific.  What is the context?  A full-trust
application should have complete access to type members through
reflection, provided you are passing the appropriate binding flags.

If for some reason your assembly has restrictions on reflection, you may
be able to use the [InternalsVisibleTo] attribute to allow your dynamic
assembly to have access to the internals of your main assembly.

Personally, I would look for a solution that somehow avoids the need for
the dynamic types to have to call the factory directly.  After all,
there's some approved way for other code to access the factory, directly
or indirectly, right?  If the dynamic types went through the same
mechanism, they shouldn't need any more access to the assembly members
than any other client code would.

But barring that, you should be able to get the reflection to work fine.
  If the above doesn't provide enough information, then provide a
concise-but-complete code example that reliably demonstrates the issue.

Pete

The unfortunate thing is that code using System.Reflection.Emit is
rarely concise. Outside of my dynamically generated classes, the only
other code that knows about the factory is code in the same DLL. It is
not like my factory is dynamically generated. I suppose I could give
you a code snippet. It is the factory class that generates
IEqualityComparers. It uses type IMetaMapping to provide information
about the type the comparer is being made for. In my situation, I am
creating an IEqualityComparer for the primary key fields of types.

// EqualityComparerRegistry.cs
using System;
using System.Reflection.Emit;
using System.Reflection;

namespace Earthworm
{
/// <summary>
/// Provides access to a single repository of dynamically
generated IEqualityComparers.
/// </summary>
internal static class EqualityComparerRegistry
{
private readonly static AssemblyBuilder _assemblyBuilder;
private readonly static ModuleBuilder _moduleBuilder;

/// <summary>
/// Initializes the AssemblyBuilder and the ModuleBuilder.
/// </summary>
static EqualityComparerRegistry()
{
AssemblyName assemblyName = new
AssemblyName("Earthworm.EqualityComparers");
_assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.RunAndCollect);
_moduleBuilder =
_assemblyBuilder.DefineDynamicModule("EarthwormEqualityComparers");
}

/// <summary>
/// Gets the dynamic module to add types to.
/// </summary>
public static ModuleBuilder Module
{
get { return _moduleBuilder; }
}
}
}

// EqualityComparerFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using Earthworm.Properties;

namespace Earthworm
{
/// <summary>
/// Creates IEqualityComparers that use the unique identifiers of
/// objects to determine equality.
/// </summary>
public sealed class EqualityComparerFactory
{
private readonly MetaMappingLookup _lookup;
private readonly Dictionary<Type, object> _cache;

/// <summary>
/// Creates a new EqualityComparerFactory.
/// </summary>
/// <param name="lookup">Provides information about the
uniquely identifying fields.</param>
public EqualityComparerFactory(MetaMappingLookup lookup)
{
_lookup = lookup;
_cache = new Dictionary<Type, object>();
}

/// <summary>
/// Creates a IEqualityComparer based on the type's unique
identifiers.
/// </summary>
/// <typeparam name="T">The type of the object to create the
comparer for.</typeparam>
/// <returns>The generated IEqualityComparer.</returns>
public IEqualityComparer<T> CreateComparer<T>(bool
holdHierachy)
{
object comparer;
if (!_cache.TryGetValue(typeof(T), out comparer))
{
IMetaMapping mapping = _lookup.GetMapping(typeof(T));
if (mapping == null)
{
return EqualityComparer<T>.Default;
}
IMemberMapping[] memberMappings =
mapping.MemberMappings.ToArray();
if (memberMappings.Length == 0)
{
return EqualityComparer<T>.Default;
}
IMemberMapping[] primaryKeyMappings =
memberMappings.Where(member => member.IsPrimaryKey).ToArray();
if (primaryKeyMappings.Length != 0)
{
memberMappings = primaryKeyMappings;
}
string typeName = typeof(T).Name + "EqualityComparer"
+ Guid.NewGuid().ToString("N");
TypeBuilder typeBuilder =
EqualityComparerRegistry.Module.DefineType(typeName,
TypeAttributes.Public | TypeAttributes.Sealed);

typeBuilder.AddInterfaceImplementation(typeof(IEqualityComparer<T>));

typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

FieldInfo fieldInfo =
typeBuilder.DefineField("_factory", typeof(EqualityComparerFactory),
FieldAttributes.Private);

defineEquals<T>(typeBuilder, memberMappings,
fieldInfo, holdHierachy);
defineGetHashCode<T>(typeBuilder, memberMappings,
fieldInfo, holdHierachy);

Type type = typeBuilder.CreateType();
comparer = Activator.CreateInstance(type);
type.GetField(fieldInfo.Name, BindingFlags.NonPublic |
BindingFlags.Instance).SetValue(comparer, this);
_cache[typeof(T)] = comparer;
}
return (IEqualityComparer<T>)comparer;
}

private void defineEquals<T>(TypeBuilder typeBuilder,
IMemberMapping[] mappings, FieldInfo fieldInfo, bool holdHierachy)
{
MethodInfo equalInfo =
typeof(IEqualityComparer<T>).GetMethod("Equals");
MethodBuilder equals = buildMethod(typeBuilder,
equalInfo);
ILGenerator generator = equals.GetILGenerator();
LocalBuilder result =
generator.DeclareLocal(typeof(Int32));
Label endOfMethod = generator.DefineLabel();

if (holdHierachy)
{
MethodInfo getTypeInfo =
typeof(Object).GetMethod("GetType");
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, getTypeInfo);
generator.Emit(OpCodes.Ldarg_2);
generator.Emit(OpCodes.Callvirt, getTypeInfo);
generator.Emit(OpCodes.Ceq);
generator.Emit(OpCodes.Brfalse, endOfMethod);
}

MethodInfo objectEquals =
typeof(Object).GetMethod("Equals", BindingFlags.Static |
BindingFlags.Public);
MethodInfo createComparerMethod =
typeof(EqualityComparerFactory).GetMethod("CreateComparer");
foreach (IMemberMapping mapping in mappings)
{
if (_lookup.Contains(mapping.MemberType))
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldInfo);
generator.Emit(OpCodes.Ldc_I4_0); // false
MethodInfo genericCreateComparerMethod =
createComparerMethod.MakeGenericMethod(new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt,
genericCreateComparerMethod);
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
generator.Emit(OpCodes.Ldarg_2);
emitMemberValue(generator, mapping);
Type comparerType =
typeof(IEqualityComparer<>).MakeGenericType(mapping.MemberType);
MethodInfo equalsMethod =
comparerType.GetMethod("Equals", new Type[] { mapping.MemberType,
mapping.MemberType });
generator.Emit(OpCodes.Callvirt, equalsMethod);
}
else
{
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
if (mapping.MemberType.IsValueType)
{
generator.Emit(OpCodes.Box,
mapping.MemberType);
}
generator.Emit(OpCodes.Ldarg_2);
emitMemberValue(generator, mapping);
if (mapping.MemberType.IsValueType)
{
generator.Emit(OpCodes.Box,
mapping.MemberType);
}
generator.Emit(OpCodes.Call, objectEquals);
}
generator.Emit(OpCodes.Brfalse, endOfMethod);
}

generator.Emit(OpCodes.Ldc_I4, 1);
generator.Emit(OpCodes.Stloc, result); // if we get this
far, we know we are equal
generator.MarkLabel(endOfMethod);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
}

private void defineGetHashCode<T>(TypeBuilder typeBuilder,
IMemberMapping[] mappings, FieldInfo fieldInfo, bool holdHierachy)
{
MethodInfo getHashCodeInfo =
typeof(IEqualityComparer<T>).GetMethod("GetHashCode");
MethodBuilder getHashCode = buildMethod(typeBuilder,
getHashCodeInfo);
ILGenerator generator = getHashCode.GetILGenerator();
LocalBuilder result =
generator.DeclareLocal(typeof(Int32));

MethodInfo objectGetHashCode =
typeof(Object).GetMethod("GetHashCode");

if (holdHierachy)
{
MethodInfo getTypeInfo =
typeof(Object).GetMethod("GetType");
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, getTypeInfo);
generator.Emit(OpCodes.Callvirt, objectGetHashCode);
generator.Emit(OpCodes.Stloc, result);
}

MethodInfo createComparerMethod =
typeof(EqualityComparerFactory).GetMethod("CreateComparer");
foreach (IMemberMapping mapping in mappings)
{
Label skip = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
generator.Emit(OpCodes.Brfalse, skip);
if (_lookup.Contains(mapping.MemberType))
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldInfo);
generator.Emit(OpCodes.Ldc_I4_0); // false
MethodInfo genericCreateComparerMethod =
createComparerMethod.MakeGenericMethod(new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt,
genericCreateComparerMethod);
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
Type comparerType =
typeof(IEqualityComparer<>).MakeGenericType(new Type[]
{ mapping.MemberType });
MethodInfo comparerGetHashCode =
comparerType.GetMethod("GetHashCode", new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt,
comparerGetHashCode);
}
else
{
generator.Emit(OpCodes.Ldarg_1);
emitMemberValue(generator, mapping);
if (mapping.MemberType.IsValueType)
{
generator.Emit(OpCodes.Box,
mapping.MemberType);
}
generator.Emit(OpCodes.Callvirt,
objectGetHashCode);
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Xor);
generator.Emit(OpCodes.Stloc, result);
generator.MarkLabel(skip);
}

generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
}

private static MethodBuilder buildMethod(TypeBuilder
typeBuilder, MethodInfo methodInfo)
{
MethodAttributes attributes = methodInfo.Attributes;
attributes &= ~(MethodAttributes.VtableLayoutMask |
MethodAttributes.Abstract);
MethodBuilder methodBuilder =
typeBuilder.DefineMethod(methodInfo.Name,
attributes,
methodInfo.CallingConvention,
methodInfo.ReturnType,
methodInfo.GetParameters().Select(parameter =>
parameter.ParameterType).ToArray());
return methodBuilder;
}

private static void emitMemberValue(ILGenerator generator,
IMemberMapping mapping)
{
if (mapping.MemberInfo.MemberType == MemberTypes.Field)
{
generator.Emit(OpCodes.Ldfld,
(FieldInfo)mapping.MemberInfo);
}
else
{
PropertyInfo propertyInfo =
(PropertyInfo)mapping.MemberInfo;
MethodInfo getterInfo = propertyInfo.GetGetMethod();
generator.Emit(OpCodes.Callvirt, getterInfo);
}
}
}
}
 
A

Arne Vajhøj

I have an internal factory class with methods I want to call from
another DLL using reflection. However, trying to do so results in a
MethodAccessException.

I am assuming this is because library vendors don't want people
accessing internal members. Is there a way around this?

In my situation, I have a factory class that generates
IEqualityComparer<T> types at runtime using System.Reflection.Emit.
The comparers can be recursive, meaning that they can rely on other
dynamically generated comparers. In order to do so, they need to call
methods on the factory.

What are my options? My factory has to be internal or public. I'd
rather avoid exposing this class if I can. I am hoping there is some
flag or attribute that tells the runtime to relax.

How do you attempt to call the method?

Normally the accessibility rules does not apply to reflection.

The following code runs:

using System;
using System.Reflection;

namespace E
{
public class Test
{
private void NoAccessMethod()
{
Console.WriteLine("What happended here?");
}
}
public class Program
{
public static void Main(string[] args)
{
Test o = new Test();
//o.NoAccessMethod();
MethodInfo mi = o.GetType().GetMethod("NoAccessMethod",
BindingFlags.DeclaredOnly | BindingFlags.NonPublic |
BindingFlags.Instance, null, new Type[0], null);
mi.Invoke(o, new object[0]);
}
}
}

Arne
 
T

Travis Parks

I have an internal factory class with methods I want to call from
another DLL using reflection. However, trying to do so results in a
MethodAccessException.
I am assuming this is because library vendors don't want people
accessing internal members. Is there a way around this?
In my situation, I have a factory class that generates
IEqualityComparer<T>  types at runtime using System.Reflection.Emit.
The comparers can be recursive, meaning that they can rely on other
dynamically generated comparers. In order to do so, they need to call
methods on the factory.
What are my options? My factory has to be internal or public. I'd
rather avoid exposing this class if I can. I am hoping there is some
flag or attribute that tells the runtime to relax.

How do you attempt to call the method?

Normally the accessibility rules does not apply to reflection.

The following code runs:

using System;
using System.Reflection;

namespace E
{
     public class Test
     {
         private void NoAccessMethod()
         {
             Console.WriteLine("What happended here?");
         }
     }
     public class Program
     {
         public static void Main(string[] args)
         {
             Test o = new Test();
             //o.NoAccessMethod();
             MethodInfo mi = o.GetType().GetMethod("NoAccessMethod",
BindingFlags.DeclaredOnly | BindingFlags.NonPublic |
BindingFlags.Instance, null, new Type[0], null);
             mi.Invoke(o, new object[0]);
         }
     }

}

Arne- Hide quoted text -

- Show quoted text -

Instead of private, I'm calling a method in an internal class. That is
the only difference.

MethodInfo createComparerMethod =
typeof(EqualityComparerFactory).GetMethod("CreateComparer"); // takes
T
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldInfo); // each comparer holds a
reference to the factory, which is supposed to be internal
generator.Emit(OpCodes.Ldc_I4_0); // false
MethodInfo genericCreateComparerMethod =
createComparerMethod.MakeGenericMethod(new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt, genericCreateComparerMethod);

Could the fact that the method is generic make a difference? The
factory itself is nongeneric, but the CreateComparer method takes a T.

I know from experience calling non-public methods is perfectly valid.
I have only run into this issue when doing stuff with
System.Reflection.Emit. Perhaps dynamically generated types have
access restrictions? Beats me.

Thanks,
Travis Parks
 
T

Travis Parks

On 06-06-2010 12:12, Travis Parks wrote:
How do you attempt to call the method?
Normally the accessibility rules does not apply to reflection.
The following code runs:
using System;
using System.Reflection;
namespace E
{
     public class Test
     {
         private void NoAccessMethod()
         {
             Console.WriteLine("What happended here?");
         }
     }
     public class Program
     {
         public static void Main(string[] args)
         {
             Test o = new Test();
             //o.NoAccessMethod();
             MethodInfo mi = o.GetType().GetMethod("NoAccessMethod",
BindingFlags.DeclaredOnly | BindingFlags.NonPublic |
BindingFlags.Instance, null, new Type[0], null);
             mi.Invoke(o, new object[0]);
         }
     }

Arne- Hide quoted text -
- Show quoted text -

Instead of private, I'm calling a method in an internal class. That is
the only difference.

MethodInfo createComparerMethod =
typeof(EqualityComparerFactory).GetMethod("CreateComparer"); // takes
T
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldInfo); // each comparer holds a
reference to the factory, which is supposed to be internal
generator.Emit(OpCodes.Ldc_I4_0); // false
MethodInfo genericCreateComparerMethod =
createComparerMethod.MakeGenericMethod(new Type[]
{ mapping.MemberType });
generator.Emit(OpCodes.Callvirt, genericCreateComparerMethod);

Could the fact that the method is generic make a difference? The
factory itself is nongeneric, but the CreateComparer method takes a T.

I know from experience calling non-public methods is perfectly valid.
I have only run into this issue when doing stuff with
System.Reflection.Emit. Perhaps dynamically generated types have
access restrictions? Beats me.

Thanks,
Travis Parks- Hide quoted text -

- Show quoted text -

Also calling a method on an internal class works using plain jane
reflection, too. Why the difference with Emit?

// load an internal class from another assembly
Assembly assembly = Assembly.LoadFrom("OtherLibrary.dll");
Type type = assembly.GetType("OtherLibrary.Class1");
object class1 = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("DoSomething");
methodInfo.Invoke(class1, null);
 
P

Peter Duniho

Travis said:
[...]
Also calling a method on an internal class works using plain jane
reflection, too. Why the difference with Emit?

Well, that's certainly the crux of the question. I don't think
dynamically generated code should in fact be different from statically
compiled code. That suggests that the error you're getting is due to
some other difference that's being obscured by the mechanism of
dynamically generating the code.

If I have some time later, I'll take a look at the code sample you
posted. I'm not actually an expert in dynamically generated code, but
sometimes all it takes is a second set of eyes looking at the problem.

Pete
 
T

Travis Parks

Peter said:
[...]
If I have some time later, I'll take a look at the code sample you
posted.  I'm not actually an expert in dynamically generated code, but
sometimes all it takes is a second set of eyes looking at the problem.

Actually, I took a preliminary look, and there are two problems with the
code example that was supposed to be concise and complete: it's neither
concise, nor complete.

You should post the bare minimum required to demonstrate the problem.
In your case, that means you need only generate a type with a single
member that is trying to access the internal member in question (or
rather, a minimal internal member, just whatever is required to actually
demonstrate the issue).

You also should post a code example that can be compiled and run without
any additional work.  For simple examples, I and others are reasonably
good at just inspecting the code and finding the problem.  But I'm sure
you'd agree that dynamic code generation does not fall into any
reasonable definition of "simple".

You may find someone else willing to try to find the problem in just the
code you posted.  But others, including myself, won't bother to look at
all unless it's convenient to actually compile and run the code so that
it can be inspected in a debugger.

Pete

Concise and complete:

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace TestCS
{
class Program
{
static void Main(string[] args)
{
AssemblyName assemblyName = new AssemblyName("TestAssm");
AssemblyBuilder assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule("TestAssm.Module");
TypeBuilder typeBuilder =
moduleBuilder.DefineType("TestAssm.DynaType", TypeAttributes.Sealed |
TypeAttributes.Public);

typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
MethodBuilder methodBuilder =
typeBuilder.DefineMethod("DynaMethod",
MethodAttributes.Public,
CallingConventions.HasThis |
CallingConventions.Standard,
typeof(void),
Type.EmptyTypes);
ILGenerator generator = methodBuilder.GetILGenerator();
ConstructorInfo ctorInfo =
typeof(Internal).GetConstructor(Type.EmptyTypes);
generator.Emit(OpCodes.Newobj, ctorInfo);
MethodInfo doSomethingInfo =
typeof(Internal).GetMethod("DoSomething");
generator.Emit(OpCodes.Callvirt, doSomethingInfo);
generator.Emit(OpCodes.Ret);
Type dynaType = typeBuilder.CreateType();
object instance = Activator.CreateInstance(dynaType);
MethodInfo dynaMethod = dynaType.GetMethod("DynaMethod");
dynaMethod.Invoke(instance, null);
}
}

internal class Internal
{
public void DoSomething()
{
}
}
}

Thanks for looking at this. It is the simplest reproduction of the
issue I could think of. I am hoping my issue is really simple.
 
A

Arne Vajhøj

Peter said:
[...]
If I have some time later, I'll take a look at the code sample you
posted. I'm not actually an expert in dynamically generated code, but
sometimes all it takes is a second set of eyes looking at the problem.

Actually, I took a preliminary look, and there are two problems with the
code example that was supposed to be concise and complete: it's neither
concise, nor complete.

You should post the bare minimum required to demonstrate the problem.
In your case, that means you need only generate a type with a single
member that is trying to access the internal member in question (or
rather, a minimal internal member, just whatever is required to actually
demonstrate the issue).

You also should post a code example that can be compiled and run without
any additional work. For simple examples, I and others are reasonably
good at just inspecting the code and finding the problem. But I'm sure
you'd agree that dynamic code generation does not fall into any
reasonable definition of "simple".

You may find someone else willing to try to find the problem in just the
code you posted. But others, including myself, won't bother to look at
all unless it's convenient to actually compile and run the code so that
it can be inspected in a debugger.

Concise and complete:

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace TestCS
{
class Program
{
static void Main(string[] args)
{
AssemblyName assemblyName = new AssemblyName("TestAssm");
AssemblyBuilder assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule("TestAssm.Module");
TypeBuilder typeBuilder =
moduleBuilder.DefineType("TestAssm.DynaType", TypeAttributes.Sealed |
TypeAttributes.Public);

typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
MethodBuilder methodBuilder =
typeBuilder.DefineMethod("DynaMethod",
MethodAttributes.Public,
CallingConventions.HasThis |
CallingConventions.Standard,
typeof(void),
Type.EmptyTypes);
ILGenerator generator = methodBuilder.GetILGenerator();
ConstructorInfo ctorInfo =
typeof(Internal).GetConstructor(Type.EmptyTypes);
generator.Emit(OpCodes.Newobj, ctorInfo);
MethodInfo doSomethingInfo =
typeof(Internal).GetMethod("DoSomething");
generator.Emit(OpCodes.Callvirt, doSomethingInfo);
generator.Emit(OpCodes.Ret);
Type dynaType = typeBuilder.CreateType();
object instance = Activator.CreateInstance(dynaType);
MethodInfo dynaMethod = dynaType.GetMethod("DynaMethod");
dynaMethod.Invoke(instance, null);
}
}

internal class Internal
{
public void DoSomething()
{
}
}
}

Thanks for looking at this. It is the simplest reproduction of the
issue I could think of. I am hoping my issue is really simple.

The problem is that you are not using reflection!

You are:

Main---(reflection)--->generated code---(regular call)--->Internal

And that fails.

Either change the accessability.

Or use reflection.

Arne
 
T

Travis Parks

Arne said:
[...]
Thanks for looking at this. It is the simplest reproduction of the
issue I could think of. I am hoping my issue is really simple.
The problem is that you are not using reflection!

What he said.  :)

So if you use direct reflection, it works. If you build something
using emit, it doesn't. I was thinking about that last night.
Essentially, I'd have to call MethodInfo.Invoke in the emitted code to
do what I want. I don't see why the runtime cares. The compiler
already checks for access. So why should the runtime? Doing reflection
when I shouldn't have to seems pretty lame.
 
A

Adam Clauss

So if you use direct reflection, it works. If you build something
using emit, it doesn't. I was thinking about that last night.
Essentially, I'd have to call MethodInfo.Invoke in the emitted code to
do what I want. I don't see why the runtime cares. The compiler
already checks for access. So why should the runtime? Doing reflection
when I shouldn't have to seems pretty lame.

The runtime needs to care - think about a different scenario:
LibraryA has a reference to, and makes calls to, LibraryB.

After LibraryA has been compiled, the author of LibraryB, for whatever
reason (they are the author after all), decides that a particular method
which used to be public should now be internal (or any other visibility
in which the code from A would not be able to call it.

A new LibraryB.dll is deployed - what should happen? The code from
LibraryA should still work as originally intended, even though LibraryB
now forbids it? That can't happen - while unfortunate for the author of
LibraryA, LibraryB is ultimately what determines which calls can and
cannot be made into it. So, the same exception you are seeing gets
thrown. This particular error cannot be caught at compilation time
unless the author of LibraryA recompiles it against the new LibraryB
(which is NOT a requirement).

So, since there are cases where the compiler cannot check it, the
runtime MUST check it to ensure the proper visibility gets used.

If you think it SHOULD be a requirement that calling libraries must be
recompiled when the libraries they reference change - think about a
patch or service pack to the core .NET Framework. Asking every software
publisher/developer writing .NET code to go recompile their applications
would never work... versioning would become (more of) a nightmare.

-Adam
 
T

Travis Parks

The runtime needs to care - think about a different scenario:
LibraryA has a reference to, and makes calls to, LibraryB.

After LibraryA has been compiled, the author of LibraryB, for whatever
reason (they are the author after all), decides that a particular method
which used to be public should now be internal (or any other visibility
in which the code from A would not be able to call it.

A new LibraryB.dll is deployed - what should happen?  The code from
LibraryA should still work as originally intended, even though LibraryB
now forbids it?  That can't happen - while unfortunate for the author of
LibraryA, LibraryB is ultimately what determines which calls can and
cannot be made into it.  So, the same exception you are seeing gets
thrown.  This particular error cannot be caught at compilation time
unless the author of LibraryA recompiles it against the new LibraryB
(which is NOT a requirement).

So, since there are cases where the compiler cannot check it, the
runtime MUST check it to ensure the proper visibility gets used.

If you think it SHOULD be a requirement that calling libraries must be
recompiled when the libraries they reference change - think about a
patch or service pack to the core .NET Framework.  Asking every software
publisher/developer writing .NET code to go recompile their applications
would never work... versioning would become (more of) a nightmare.

-Adam

You make a good point. However, what causes this scenarios in the
first place? Do these scenarios come up when a dynamic type is working
on types in the assembly that created it?
 
T

Travis Parks

You make a good point. However, what causes this scenarios in the
first place? Do these scenarios come up when a dynamic type is working
on types in the assembly that created it?- Hide quoted text -

- Show quoted text -

By the way, I did find a workaround: the InternalsVisibleTo attribute.
It is an assembly-level attribute. I can pass to it the name of my
dynamic assembly. I tried it and my unit tests are still passing.

Thanks for everyone's help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top