Roslyn: detect when a local variable has been declared with `using` - c#

I'm writing a C# source generator (similar to analyzer) and I'm trying to determine if a local variable (ILocalSymbol) is readonly due to being declared with a using directive. Specifically the following case:
using System;
struct Container : IDisposable
{
public void Dispose() {}
}
public class C {
public void M() {
using (var container = new Container())
{
var otherCon = new Container();
// I want to detect when the following would throw an error
//container = otherCon;
}
}
}
Roslyn doesn't seem to have any public APIs for this as far as I can tell. LocalSymbol has IsUsing but that is an internal type. Same deal with DeclarationKind.

The only way I found to do this was by examining the declaring syntax for the variable:
var isWritable = true;
var declaringSyntax = symbol.OriginalDefinition.DeclaringSyntaxReferences.FirstOrDefault();
if (declaringSyntax?.GetSyntax().Parent is VariableDeclarationSyntax
variableDeclarationSyntax && variableDeclarationSyntax?.Parent is UsingStatementSyntax)
isWritable = false;

Related

Why are closures different for variables from C# 8.0 using declarations?

I've noticed a difference in the way the C# 8.0 compiler builds closure classes for captured IDisposable variables that are declared with a C# 8.0 using declaration, as opposed to variables declared with the classic using statement.
Consider this simple class:
public class DisposableClass : IDisposable
{
public void Dispose() { }
}
And this sample code:
public void Test()
{
using var disposable1 = new DisposableClass();
using var disposable2 = new DisposableClass();
Action action = () => Console.Write($"{disposable1}{disposable2}");
}
The compiler generates this code:
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
public DisposableClass disposable1;
public DisposableClass disposable2;
internal void <Test>b__0()
{
Console.Write(string.Format("{0}{1}", disposable1, disposable2));
}
}
public void Test()
{
<>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
<>c__DisplayClass0_.disposable1 = new DisposableClass();
try
{
<>c__DisplayClass0_.disposable2 = new DisposableClass();
try
{
Action action = new Action(<>c__DisplayClass0_.<Test>b__0);
}
finally
{
if (<>c__DisplayClass0_.disposable2 != null)
{
((IDisposable)<>c__DisplayClass0_.disposable2).Dispose();
}
}
}
finally
{
if (<>c__DisplayClass0_.disposable1 != null)
{
((IDisposable)<>c__DisplayClass0_.disposable1).Dispose();
}
}
}
This looks perfectly ok. But then I noticed that if I declare those two variables with a using statement, the closure class is generated pretty differently. This is the sample code:
public void Test()
{
using (var disposable1 = new DisposableClass())
using (var disposable2 = new DisposableClass())
{
Action action = () => Console.Write($"{disposable1}{disposable2}");
}
}
And this is what I get:
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
public DisposableClass disposable1;
}
[CompilerGenerated]
private sealed class <>c__DisplayClass0_1
{
public DisposableClass disposable2;
public <>c__DisplayClass0_0 CS$<>8__locals1;
internal void <Test>b__0()
{
Console.Write(string.Format("{0}{1}", CS$<>8__locals1.disposable1, disposable2));
}
}
Why does this happen? The rest of the code looks identical, and I thought that a using declaration was supposed to be exactly the same as a using statement that considered as block the current block it's declared within.
Not to mention that the way the closure class is generated for using declarations looks way clearer, and most importantly, much easier to explore through reflection.
I'd love some insights, if anyone knows why this is happening.
Thanks!
The compiler is generating a [CompilerGenerated] class for each "scope"... In the first example, there is a single scope, the whole Test() method. In the second example (that you don't give), there are two scopes, the two using.
The code of the second example is probably:
public void Test()
{
using (var disposable1 = new DisposableClass())
{
using (var disposable2 = new DisposableClass())
{
Action action = () => Console.Write($"{disposable1}{disposable2}");
}
}
}
As noted by juharr, these two blocks of code produce the same code:
using (DisposableClass disposable1 = new DisposableClass(), disposable2 = new DisposableClass())
{
Action action = () => Console.Write($"{disposable1}{disposable2}");
}
and
using var disposable1 = new DisposableClass();
using var disposable2 = new DisposableClass();
Action action = () => Console.Write($"{disposable1}{disposable2}");

Add Custom Class at runtime, Reference missing

i'm trying to generate Code at runtime which uses a custom class from another Namespace.
Here´s my code:
namespace Test.Programm.Network
{
class Handler
{
public void CreateAssembly()
{
string[] code =
{
#"using System;
using System.Collections;
namespace Test.Programm.Network
{
class HandleMessage
{
protected static internal Queue _queue;
public static void Received(string message)
{
lock (_queue)
{
_queue.Enqueue(message);
}
}
public HandleMessage()
{
_queue = new Queue();
}
}
}"
};
CompilerParameters parms = new CompilerParameters();
parms.GenerateExecutable = false;
parms.GenerateInMemory = true;
CodeDomProvider compiler = null;
compiler = CodeDomProvider.CreateProvider("CSharp");
CompilerResults compilerResults = compiler.CompileAssemblyFromSource(parms, code);
var cls = compilerResults.CompiledAssembly.GetType("Test.Programm.Network.HandleMessage");
Assembly assembly = compilerResults.CompiledAssembly;
var newHandler = assembly.CreateInstance(compilerResults.CompiledAssembly.GetType("Test.Programm.Network.HandleMessage").ToString());
}
}
}
But i don´t want to pass a string to my function, i want to pass an own type to that function.
Now i have a simple message class like that:
namespace Test.Programm.Messages
{
public class Message<T>
{
string _message;
}
}
if i want too add a using Test.Programm.Messages to the code i want to generate, i´m getting error that this Namespace doesn´t exist, missing reference...
I tried to add parms.ReferencedAssemblies.Add("Grid.Node.Messages"); to the code Generation, but this doesnt work. searching the web and SO haven´t given an answer yet -.-
Thanks for your help.
You should reference the assembly instead of the namespace. Something like this:
parms.ReferencedAssemblies.Add("path_to.dll");
Where path_to.dll is a path to the assembly file containing type Message<T>.

Variable not saving data in C#

i've been wondering why my variable is not saving data. this is my code
class MainProg
{
public string name;
static void Main()
{
MainProg m = new MainProg();
m.Start();
}
public void Start()
{
Register rs = new Register();
Register r = (Register)rs;
r.run();
Console.WriteLine(name);
}
}
class Register : MainProg
{
public void run()
{
name = "a";
}
}
Did I forget anything?everytime I try to show the output it shows nothing.Thanks by the way for taking your time.
This will work.
class MainProg
{
static void Main()
{
Register rs = new Register();
Register r = (Register)rs;
r.run();
Console.WriteLine(r.name);
}
}
class Register : MainProg
{
public string name;
public void run()
{
name = "a";
}
}
The reason your code fails is that you are creating an instance of Register, which is separate from MainProg even though it inherits from it. You set the variable in the new instance, and then read it from the old.
name is an instance field so you need reference of an object.
Try,
Console.WriteLine(r.name);
Because you are changing the name inside the instance of Register called r then you print the var name inside the instance of MainProg.
To prove it try:
Console.WriteLine(r.name);
Since you are trying to access a member variable inside a static function it will throw an compilation error.
Try to access the variable using object like
Console.WriteLine(r.name);
The problem is that you are trying to write the name from this object. The variable you changed is located in the other object 'r'.

does enterprise library 5.0 cache mappings between datareader and custom classes for accessor methods

Wanted to know if the Accessor methods of Enterprise Library 5.0 cache the fields of datareader as well as custom classes for performance such that it does not look up field names on custom classes using reflections and does not look up field names on datareader when mapping datareader to objects? Because its a pretty expensive operation to map custom class fields to datareader fields for every access / code block
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
var r = db.ExecuteSqlStringAccessor<Region>("SELECT * FROM Region");
}
}
public class Region
{
public string RegionnId { get; set; }
public string Name { get; set; }
}
From the code, that method goes via:
public static IEnumerable<TResult> ExecuteSqlStringAccessor<TResult>(this Database database, string sqlString)
where TResult : new()
{
return CreateSqlStringAccessor<TResult>(database, sqlString).Execute();
}
then to
IRowMapper<TResult> defaultRowMapper = MapBuilder<TResult>.BuildAllProperties();
which goes via
return MapAllProperties().Build();
which is:
public static IMapBuilderContext<TResult> MapAllProperties()
{
IMapBuilderContext<TResult> context = new MapBuilderContext();
var properties =
from property in typeof(TResult).GetProperties(BindingFlags.Instance | BindingFlags.Public)
where IsAutoMappableProperty(property)
select property;
foreach (var property in properties)
{
context = context.MapByName(property);
}
return context;
}
so no; I see no evidence of any caching there. You could add some, or you could use domething that already does materializer and parameterization caching (*cough* dapper-dot-net *cough*)
Here is an easy and nice hack suggested by entlib support team (you can check the full thread at http://entlib.codeplex.com/discussions/281833):
randylevy Mon at 11:39 PM No, there isn't any caching of the
RowMapper. The only caching I'm aware of for the Data Access
Application Block is stored procedure parameter caching.
If you are using the default mapper then you could cache the results
yourself and pass into the ExecuteSqlStringAccessor method since it
supports IRowMapper and IResultSetMapper overloads.
E.g.:
public class RowMapperCache
{
private Dictionary<Type, object> cache = new Dictionary<Type, object>();
private object locker = new object();
public IRowMapper<T> GetCachedMapper<T>() where T : new()
{
Type type = typeof(T);
lock (locker)
{
if (!Contains(type))
{
cache[type] = MapBuilder<T>.BuildAllProperties();
}
}
return cache[type] as IRowMapper<T>;
}
private bool Contains(T type)
{
return cache.ContainsKey(type);
}
}
// retrieve default mapper and cache it
IRowMapper<Region> regionMapper = rowMapperCache.GetCachedMapper<Region>();
var db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
var r = db.ExecuteSqlStringAccessor<Region>("SELECT * FROM Region", regionMapper);
UPDATE From EntLib again (an even better solution):
Thanks. Maybe, it can be put on the table for Enterprise Library 6
since it seems like a good idea?
Just for fun, I refined the example a bit to store the RowMapperCache
as a singleton inside of the EnterpriseLibraryContainer so that it can
be retrieved similar to other Enterprise Library objects. Although
not an Enterprise Library "native" class, the RowMapperCache is used
only with Enterprise Library so it's not a huge leap to store it in
the container (especially if you aren't using full Unity IoC).
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel.Unity;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;
namespace RowMapperConsole
{
public class Region {}
public class RowMapperCache
{
private Dictionary<Type, object> cache = new Dictionary<Type, object>();
private object locker = new object();
public IRowMapper<T> GetCachedMapper<T>() where T : new()
{
Type type = typeof(T);
lock (locker)
{
if (!Contains(type))
{
cache[type] = MapBuilder<T>.BuildAllProperties();
}
}
return cache[type] as IRowMapper<T>;
}
private bool Contains(T type)
{
return cache.ContainsKey(type);
}
}
class Program
{
static void Main(string[] args)
{
ApplicationInitialize();
// ...
IEnumerable<Region> regions = GetRegions();
}
public static void ApplicationInitialize()
{
ConfigureContainer(container =>
{
// Register as Singleton
container.RegisterType<RowMapperCache>(new ContainerControlledLifetimeManager());
});
}
public static void ConfigureContainer(Action<IUnityContainer> action)
{
IUnityContainer container = new UnityContainer();
if (action != null)
action(container);
IContainerConfigurator configurator = new UnityContainerConfigurator(container);
EnterpriseLibraryContainer.ConfigureContainer(configurator, ConfigurationSourceFactory.Create());
IServiceLocator locator = new UnityServiceLocator(container);
EnterpriseLibraryContainer.Current = locator;
}
public static IEnumerable<Region> GetRegions()
{
IRowMapper<Region> regionMapper = EnterpriseLibraryContainer.Current.GetInstance<RowMapperCache>()
.GetCachedMapper<Region>();
var db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
return db.ExecuteSqlStringAccessor<Region>("SELECT * FROM Region", regionMapper).ToList();
}
}
}

MEF Import from a Castle Kernel in Silverlight

I'm currently using MEF in my project, however, a legacy component uses Castle to export all its components.
I would like to be able to Import from this kernel when creating new objects, in addition to getting the exports from the Xap.
Is this possible? Can you show me some example code?
MEF was designed to be as flexible as possible, and one of its secretly hidden but real nice features, is the ability to define new ExportProvider instances, that allow you to plug in additional components. I've talked about this previously by utilising the Common Service Locator project in an ASP.NET MVC with MEF Project (see part 3 here).
The CSL is a nice flexible approach, as there are many specific CSL implementations for many of the existing IoC containers, such as Castle, Autofac, Ninject, Unity etc.
Another good example can be found here, which demonstrates a slightly different, but fundamentally similar approach.
As Matthew correctly said, the way to do this is using an ExportProvider
Another example is here (it demonstrates exports from Xaml).
Below is what I did in the end to solve the problem.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
namespace MEFCastleBridge
{
public class CastleExportProvider : ExportProvider
{
WindsorContainer _container;
private readonly Dictionary<ExportDefinition, List<Export>> _exports =
new Dictionary<ExportDefinition, List<Export>>();
private readonly object _sync = new object();
public CastleExportProvider(WindsorContainer container)
{
_container = container;
var handlers = _container.Kernel.GetAssignableHandlers(typeof(object));
foreach (var handler in handlers)
{
RegisterCastleComponent(handler);
}
_container.Kernel.ComponentRegistered += ComponentRegistered;
}
protected override IEnumerable<Export> GetExportsCore(
ImportDefinition definition, AtomicComposition atomicComposition)
{
var contractDefinition = definition as ContractBasedImportDefinition;
var retVal = Enumerable.Empty<Export>();
if (contractDefinition != null)
{
string contractName = contractDefinition.ContractName;
if (!string.IsNullOrEmpty(contractName))
{
var exports =
from e in _exports
where string.Compare(e.Key.ContractName, contractName, StringComparison.OrdinalIgnoreCase) == 0
select e.Value;
if (exports.Count() > 0)
{
retVal = exports.First();
}
}
}
return retVal;
}
void RegisterCastleComponent(IHandler handler)
{
var type = handler.Service;
var contractName = type.ToString();
lock (_sync)
{
var found = from e in _exports
where string.Compare(e.Key.ContractName,
contractName, StringComparison.OrdinalIgnoreCase) == 0
select e;
if (found.Count() == 0)
{
var metadata = new Dictionary<string, object>();
var definition = new ExportDefinition(contractName, metadata);
_exports.Add(definition, new List<Export>());
}
var wrapper = new Export(contractName, () => _container.Resolve(type));
found.First().Value.Add(wrapper);
}
}
void ComponentRegistered(string key, IHandler handler)
{
RegisterCastleComponent(handler);
}
}
public interface IMyComponent
{
string TheString { get; }
}
public class RegisteredComponent : IMyComponent
{
public string TheString { get { return "RegisteredComponent"; } }
}
[Export(typeof(IMyComponent))]
public class ExportedComponent : IMyComponent
{
public string TheString { get { return "ExportedComponent"; } }
}
public class ExportExample
{
// Will contain an instance of RegisteredComponent and ExportedComponent
[ImportMany]
public List<IMyComponent> Components { get; set; }
public ExportExample()
{
// Create a Windsor container and add a type.
var container = new WindsorContainer();
container.Register(Component.For<IMyComponent>().ImplementedBy<MyComponent>().LifeStyle.Singleton);
// Add the Export Provider, in addition to the DeploymentCatalog
var compContainer = new CompositionContainer(new DeploymentCatalog(), new CastleExportProvider(container));
// Should only be called once, before any attempt to SatisfyImports.
CompositionHost.Initialize(compContainer);
CompositionInitializer.SatisfyImports(this);
Test = string.Join(", ", Components.Select(c => c.DoSomething));
}
public string Test { get; set; }
}
}

Categories