DEV Community

Cover image for Visual Studio/Copilot # tip
Karen Payne
Karen Payne

Posted on

Visual Studio/Copilot # tip

Introduction

Learn how to use #file for referencing files known as context variables feature with asking a question using inline version of Copilot which also works in Copilot Chat.

Goal

Although Copilot can write all code required for the task below with several prompts, the goal is not to get hung up on writing the perfect prompt but instead to have Copilot to write the bulk of the work which below is in a for statement.

Once Copilot has, in this case nailed the solution the developer is now free to make changes like exception handling and optionally use asynchronous read operation rather than synchronous read operation.

So the goal is to teach developers how to be smart about writing/engineering A.I. prompts to write difficult code (dependent on developer experience) and leave tweaks to the code to the developer, not Copilot.

GitHub source gist

💡 Make sure to check out resources at end of the article.

This article uses information from this article while here the focus also include the goal, short and to the point prompts.

Task

A developer is provided the following file structure which needs to read the data into an array using the following class/model.

File structure

  • There are empty lines
  • In Visual Studio project, the file is at the root of the project
    • The file property Copy to Output folder is set to copy if newer file structure

Model

internal class Friend
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Let's ask Copilot to read the file. In any file in the project, press ALT + / which brings up Copilot.

Type the following

read # which will bring up a list of files, either traverse the list or start typing people followed by selecting people.

Next, add a space and type into followed by # and select Friends.cs same as for people.txt.

Next, type using a new class named FileOperations.

The complete question/prompt.

question/prompt for above instructions

Next, press ENTER or click the paper airplane.

Copilot responds with;

internal class FileOperations
{
    public static List<Friend> ReadFriendsFromFile(string filePath)
    {
        List<Friend> friends = new List<Friend>();

        string[] lines = File.ReadAllLines(filePath);

        for (int i = 0; i < lines.Length; i += 4)
        {
            string firstName = lines[i].Trim();
            string lastName = lines[i + 1].Trim();
            DateTime birthDate = DateTime.Parse(lines[i + 2].Trim());

            Friend friend = new Friend
            {
                FirstName = firstName,
                LastName = lastName,
                BirthDate = birthDate
            };

            friends.Add(friend);
        }

        return friends;
    }
}
Enter fullscreen mode Exit fullscreen mode

🛑 Perfect but we need to take into consider empty lines. This was left out intentionally to keep Copilot focused on the task.

Change

string[] lines = File.ReadAllLines(filePath);
Enter fullscreen mode Exit fullscreen mode

To

string[] lines = File.ReadAllLines(filePath)
    .Where(line => !string.IsNullOrEmpty(line))
    .ToArray();
Enter fullscreen mode Exit fullscreen mode

Now try reading the file using the following.

var friends = FileOperations.ReadFriendsFromFile("people.txt");
Enter fullscreen mode Exit fullscreen mode

Refinement 1

Once satisfied with the code above, lets make some minor changes.

Since the birth date has no time, change the property BirthDate to DateOnly.

internal class Friend
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateOnly BirthDate { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Next, in FileOperations.ReadFriendsFromFile change.

DateTime birthDate = DateTime.Parse(lines[i + 2].Trim());
Enter fullscreen mode Exit fullscreen mode

To

DateOnly birthDate =  DateOnly.FromDateTime(DateTime.Parse(lines[i + 2].Trim()));
Enter fullscreen mode Exit fullscreen mode

Next rename ReadFriendsFromFile method name to ReadFriends as FromFile is not needed as the class name indicates we are dealing with files.

Refinement 2

Let's change the variable i to index which is easier to read.

internal class FileOperations
{
    public static List<Friend> ReadFriends(string filePath)
    {
        List<Friend> friends = new List<Friend>();

        string[] lines = File.ReadAllLines(filePath)
            .Where(line => !string.IsNullOrEmpty(line))
            .ToArray();

        for (int index = 0; index < lines.Length; index += 4)
        {
            string firstName = lines[index].Trim();
            string lastName = lines[index + 1].Trim();
            DateOnly birthDate =  DateOnly.FromDateTime(DateTime.Parse(lines[index + 2].Trim()));

            Friend friend = new Friend
            {
                FirstName = firstName,
                LastName = lastName,
                BirthDate = birthDate
            };

            friends.Add(friend);
        }

        return friends;
    }
}
Enter fullscreen mode Exit fullscreen mode

Refinement 3

Suppose, in production the file is missing, the file exists but the data is malformed? We should add a try/catch as shown below which changed the return type from a list to a tuple of list and exception.

internal class FileOperations
{
    public static (List<Friend> data, Exception exception) ReadFriends(string filePath)
    {
        List<Friend> friends = new List<Friend>();

        try
        {
            string[] lines = File.ReadAllLines(filePath)
                .Where(line => !string.IsNullOrEmpty(line))
                .ToArray();

            for (int index = 0; index < lines.Length; index += 4)
            {
                string firstName = lines[index].Trim();
                string lastName = lines[index + 1].Trim();
                DateOnly birthDate = DateOnly.FromDateTime(DateTime.Parse(lines[index + 2].Trim()));

                Friend friend = new Friend
                {
                    FirstName = firstName,
                    LastName = lastName,
                    BirthDate = birthDate
                };

                friends.Add(friend);
            }

            return (friends, null);
        }
        catch (Exception ex)
        {
            return (null, ex);
        }

    }
}
Enter fullscreen mode Exit fullscreen mode

When making the above changes yourself note Visual Studio will lite code up in red until finished.

Next change the return type as follows.

var (friends, exception) = FileOperations.ReadFriends("people.txt");
if (exception is null)
{
    // read data
}
else
{
    // handle exception
}
Enter fullscreen mode Exit fullscreen mode

Refinement 4

If the file has a lot of data, consider reading the file asynchronously.

internal class FileOperations
{
    public static async Task<(List<Friend> data, Exception exception)> ReadFriends(string filePath)
    {
        List<Friend> friends = new List<Friend>();

        try
        {
            string[] lines = (await File.ReadAllLinesAsync(filePath))
                .Where(line => !string.IsNullOrWhiteSpace(line))
                .ToArray();


            for (int index = 0; index < lines.Length; index += 4)
            {
                string firstName = lines[index].Trim();
                string lastName = lines[index + 1].Trim();
                DateOnly birthDate = DateOnly.FromDateTime(DateTime.Parse(lines[index + 2].Trim()));

                Friend friend = new Friend
                {
                    FirstName = firstName,
                    LastName = lastName,
                    BirthDate = birthDate
                };

                friends.Add(friend);
            }

            return (friends, null);
        }
        catch (Exception ex)
        {
            return (null, ex);
        }

    }
}
Enter fullscreen mode Exit fullscreen mode

And change the calling code to

var (friends, exception) = await FileOperations.ReadFriends("people.txt");
if (exception is null)
{
    // read data
}
else
{
    // handle exception
}
Enter fullscreen mode Exit fullscreen mode

Resources

Top comments (1)

Collapse
 
bierbal profile image
ron

Very nice example! Good instructions step by step and also the refactoring! Love it.